Это хороший субстрат для C? - PullRequest
1 голос
/ 17 мая 2009

См. Также C Tokenizer


Вот быстрый substr () для 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-)
[*]'' (")
[*]'' (")

Ответы [ 5 ]

7 голосов
/ 17 мая 2009

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

  • strdup() и другие функции выделения памяти, может дать сбой, вы должны разрешить все возможные проблемы.
  • выделяет ресурсы (в данном случае память) только тогда, когда вам это нужно.
  • Вы должны быть в состоянии различать ошибки и действительные строки. В настоящий момент вы не знаете, приводит ли malloc() сбой substr ("xxx",1,1) или рабочий substr ("xxx",1,0) к пустой строке.
  • вам не нужно calloc() память, которую вы все равно собираетесь перезаписать.
  • все недопустимые параметры должны либо вызывать ошибку, либо приводиться к допустимому параметру (и ваш API должен задокументировать, какой именно).
  • вам не нужно устанавливать для локального emptyString значение NULL после его освобождения - оно будет потеряно при возврате функции.
  • вам не нужно вводить strncat() - вы должны знать размеры и доступную память, прежде чем делать какое-либо копирование, чтобы вы могли использовать (скорее всего) быстрее memcpy().
  • вы используете base-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;
}
5 голосов
/ 17 мая 2009

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

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

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

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

РЕДАКТИРОВАТЬ: Что касается ваших обновлений:

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

Я также хотел бы повторить, что согласованность с большинством других кодов C означает возвращение NULL при ошибке, а не "". Я знаю, что многие функции (например, strcmp()), вероятно, будут делать плохие вещи, если вы передадите им NULL - этого следовало ожидать. Но стандартная библиотека C (и многие другие API C) используют подход: «Ответственность за проверку 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);

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

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

РЕДАКТИРОВАТЬ 2: три строки кода не делают то же самое, но в этом контексте они все будут давать один и тот же результат. Спасибо, что поймали меня на этом.

1 голос
/ 17 мая 2009
char* emptyString = strdup(""); /* C'mon! This cannot fail? */

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

0 голосов
/ 02 августа 2012

Вы также можете использовать функцию 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.

0 голосов
/ 17 мая 2009

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

...