2009-05-17 3 views
1

Смотрите также C TokenizerЯвляется ли это хорошим субстратом для C?


Вот быстрый зиЬзЬг() для C, что я написал (да, переменная инициализации должна быть перемещена в начале функции и т.д., но вы получите идею)

Я видел много «умных» реализаций substr(), которые простые однолинейные вызовы strncpy()!

Они все неправильно (strncpy не гарантирует нулевое окончание и, таким образом, вызов может НЕ произвести правильную подстроку!)

Вот что-то может быть лучше?

Вывести ошибки!

char* substr(const char* text, int nStartingPos, int nRun) 
{ 
    char* emptyString = strdup(""); /* C'mon! This cannot fail */ 

    if(text == NULL) return emptyString; 

    int textLen = strlen(text); 

    --nStartingPos; 

    if((nStartingPos < 0) || (nRun <= 0) || (textLen == 0) || (textLen < nStartingPos)) return emptyString; 

    char* returnString = (char *)calloc((1 + nRun), sizeof(char)); 

    if(returnString == NULL) return emptyString; 

    strncat(returnString, (nStartingPos + text), nRun); 

    /* We do not need emptyString anymore from this point onwards */ 

    free(emptyString); 
    emptyString = NULL; 

    return returnString; 
} 


int main() 
{ 
    const char *text = "-2--4--6-7-8-9-10-11-"; 

    char *p = substr(text, -1, 2); 
    printf("[*]'%s' (\")\n", ((p == NULL) ? "<NULL>" : p)); 
    free(p); 

    p = substr(text, 1, 2); 
    printf("[*]'%s' (-2)\n", ((p == NULL) ? "<NULL>" : p)); 
    free(p); 

    p = substr(text, 3, 2); 
    printf("[*]'%s' (--)\n", ((p == NULL) ? "<NULL>" : p)); 
    free(p); 

    p = substr(text, 16, 2); 
    printf("[*]'%s' (10)\n", ((p == NULL) ? "<NULL>" : p)); 
    free(p); 

    p = substr(text, 16, 20); 
    printf("[*]'%s' (10-11-)\n", ((p == NULL) ? "<NULL>" : p)); 
    free(p); 

    p = substr(text, 100, 2); 
    printf("[*]'%s' (\")\n", ((p == NULL) ? "<NULL>" : p)); 
    free(p); 

    p = substr(text, 1, 0); 
    printf("[*]'%s' (\")\n", ((p == NULL) ? "<NULL>" : p)); 
    free(p); 

    return 0; 
} 

Выход:

[*]'' (") 
[*]'-2' (-2) 
[*]'--' (--) 
[*]'10' (10) 
[*]'10-11-' (10-11-) 
[*]'' (") 
[*]'' (") 
+0

Пока мы на нем, printf ("% s \ n", NULL) будет печатать "(null) \ n", а не сбой в какой-либо массовой ошибке, поэтому вам не нужно проверять, NULL внутри вашего printf() s. –

+0

На самом деле, я не могу найти что-либо в каких-либо стандартах относительно поведения printf() при задании NULL как строки, но это то, что он делает в моей системе (OS X Leopard). –

+0

Да, Chris, у меня тоже есть (нуль) на всех ОС, над которыми я работал, но затем strcmp() с NULL сбрасываемым ядром на Solaris (или это был HP-UX?), Но он не делает этого на других. так зачем беспокоиться с небольшой тройной? – PoorLuzer

ответ

5

Я бы сказал, что возвращает NULL, если вход недействителен, а не malloc() пустой пустой строки. Таким образом, вы можете проверить, не сработала или нет функция с if(p), а не if(*p == 0).

Кроме того, я думаю, что ваша функция утечки памяти, потому что emptyString только free() d в одном условном. Вы должны удостовериться, что вы это сделали безоговорочно, т. Е. Прямо перед return.

Как на ваш комментарий strncpy() не NUL оконечное строку (что верно), если вы используете calloc() выделить строку, а не malloc(), это не будет проблемой, если вы выделить один байт больше, чем скопировать , так как calloc() автоматически устанавливает все значения (включая, в данном случае, конец), на 0.

Я бы дал вам больше заметок, но мне не нравится читать код camelCase. Не то, чтобы в этом что-то не так.

EDIT: Что касается ваших обновлений:

Имейте в виду, что стандарт C определяет sizeof(char) быть 1, независимо от вашей системы. Если вы используете компьютер, который использует 9 бит в байте (бог запрещает), sizeof(char) все еще будет 1. Не то, чтобы что-то не так с высказыванием sizeof(char) - он четко показывает ваше намерение и обеспечивает симметрию вызовами calloc() или malloc() для других типов. Но sizeof(int) действительно полезен (int s могут быть разных размеров на 16- и 32- и эти новомодные 64-разрядные компьютеры). Чем больше ты знаешь.

Я также хотел бы подтвердить, что согласованность с большинством других C-кода заключается в возврате NULL на ошибку, а не на "". Я знаю, что многие функции (например, strcmp()), вероятно, сделают плохие вещи, если вы передадите им NULL - этого и следовало ожидать.Но стандартная библиотека C (и многие другие C API-интерфейсы) использует подход «Ответственность звонящего за проверку NULL, а не ответственность функции за его/ее ребенка, если он этого не сделает». Если вы хотите сделать это по-другому, это круто, но это противоречит одной из самых сильных тенденций в дизайне интерфейса C.

Кроме того, я бы использовал strncpy() (или memcpy()), а не strncat(). Использование strncat()strcat()) скрывает ваше намерение - это заставляет кого-то смотреть на ваш код думать, что вы хотите добавить в конец строки (что вы делаете, потому что после calloc() конец - это начало), когда то, что вы хотите сделать задается строка. strncat() делает вид, что вы добавляете строку, в то время как strcpy() (или другая процедура копирования) заставит его больше походить на то, что вы намерены. Следующие три строки все делают то же самое в этом контексте - выбрать в зависимости от того, который вы думаете, выглядит самый хороший:

strncat(returnString, text + nStartingPos, nRun); 

strncpy(returnString, text + nStartingPos, nRun); 

memcpy(returnString, text + nStartingPos, nRun); 

Plus, strncpy() и memcpy(), вероятно, будет (крошечное мало) немного быстрее/эффективнее, чем strncat().

text + nStartingPos такая же, как nStartingPos + text - я бы поставил char * первый, как я думаю, что это ясно, но независимо от того, вы хотите, чтобы поместить их в это до вас. Кроме того, круглые скобки вокруг них не нужны (но приятно), поскольку + имеет более высокий приоритет, чем ,.

EDIT 2: Три строки кода не делают то же самое, но в этом контексте все они будут давать одинаковый результат. Спасибо, что поймал меня на этом.

+0

+1, .. ОТЛИЧНО писать, кстати, Крис! – PoorLuzer

+0

Неверное утверждение о трех строках кода. strncat предоставляется суффикс 0 после копирования строки источника. strncpy не может быть суффиксом 0 после копирования, но он прекратит процесс копирования, как только он натолкнется на первый \ 0 .. .. и все мы знаем, что memcpy :-) – PoorLuzer

+0

Они не то же самое , но в этом контексте все они будут давать одинаковый результат. Я хочу сказать, что strncat делает много лишних (и в этом случае ненужных) работы, чем strncpy или memcpy. –

1
char* emptyString = strdup(""); /* C'mon! This cannot fail? */ 

Вы должны проверить нуль. Помните, что он все равно должен выделять 1 байт для нулевого символа.

+0

не понял! – PoorLuzer

0

strdup может потерпеть неудачу (хотя это очень маловероятно и не стоит проверять, ИМХО). Однако у него есть другая проблема - это не стандартная функция C. Было бы лучше использовать malloc.

+0

Для чего стоит, strdup() достаточно легко написать, чтобы вы могли его использовать, и использовать autoconf или что-то подобное, чтобы проверить, нужно ли вам перевернуть свою собственную версию. –

+0

strdup теперь удален .. люди были слишком педантичны ;-) – PoorLuzer

7

Ваша функция кажется очень сложной для того, что должно быть простой операцией. Некоторые проблемы (не все из них являются ошибками):

  • strdup() и другие функции распределения памяти, может неудачу, вы должны учесть все возможные проблемы.
  • только выделять ресурсы (память в этом случае), если и когда вам это нужно.
  • вы должны иметь возможность различать ошибки и действительные укусы. На данный момент вы не знаете, произошел ли отказ malloc()substr ("xxx",1,1) или рабочий substr ("xxx",1,0) создает пустую строку.
  • вам не нужно calloc() память, которую вы все равно перепишете.
  • все недопустимые параметры должны либо вызывать ошибку, либо приводиться в действие к допустимому параметру (и ваш API должен документировать, что).
  • вам не нужно устанавливать локальную пустую строку в NULL после ее освобождения - она ​​будет потеряна при возврате функции.
  • Вам не нужно использовать usr strncat() - вы должны знать размеры и доступную память перед выполнением любого копирования, чтобы вы могли быстрее использовать (скорее всего) memcpy().
  • Вы используете базовый-1, а не base-0 для смещений строк, против зерна C.

Следующий сегмент - это то, что я сделал бы (мне больше нравится идиома Python отрицательных значений для подсчета с конца строки, но я сохранил длину, а не конечную позицию).

char *substr (const char *inpStr, int startPos, int strLen) { 
    /* Cannot do anything with NULL. */ 

    if (inpStr == NULL) return NULL; 

    /* All negative positions to go from end, and cannot 
     start before start of string, force to start. */ 

    if (startPos < 0) 
     startPos = strlen (inpStr) + startPos; 
    if (startPos < 0) 
     startPos = 0; 

    /* Force negative lengths to zero and cannot 
     start after end of string, force to end. */ 

    if (strLen < 0) 
     strLen = 0; 
    if (startPos >strlen (inpStr)) 
     startPos = strlen (inpStr); 

    /* Adjust length if source string too short. */ 

    if (strLen > strlen (&inpStr[startPos])) 
     strLen = strlen (&inpStr[startPos]); 

    /* Get long enough string from heap, return NULL if no go. */ 

    if ((buff = malloc (strLen + 1)) == NULL) 
     return NULL; 

    /* Transfer string section and return it. */ 

    memcpy (buff, &(inpStr[startPos]), strLen); 
    buff[strLen] = '\0'; 

    return buff; 
} 
+0

+1, ОТЛИЧНАЯ запись! Благодаря! – PoorLuzer

0

Вы также можете использовать функцию memmove для возврата подстроки от начала в длину. оздоровительных/добавление другой раствора из раствора paxdiablo в:

#include <stdlib.h> 
    #include <stdio.h> 
    #include <string.h> 

    char *splitstr(char *idata, int start, int slen) { 
      char ret[150]; 
      if(slen == NULL) { 
        slen=strlen(idata)-start; 
      } 
      memmove (ret,idata+start,slen); 
      return ret; 
    } 

    /* 
    Usage: 
      char ostr[]="Hello World!"; 
      char *ores=splitstr(ostr, 0, 5); 
      Outputs: 
        Hello 
    */ 

Надеется, что это помогает. Протестировано на Windows 7 Home Premium с TCC C Compilier.

Смежные вопросы