Вопрос

Смотрите также C токенизатор


Вот быстрый метод 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-)
[*]'' (")
[*]'' (")
Это было полезно?

Решение

Я бы сказал возвращение 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) действительно полезно(ints могут быть разных размеров на 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:Три строки кода не делают одно и то же, но в данном контексте все они дадут один и тот же результат.Спасибо, что поймали меня на этом.

Другие советы

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

  • strdup(), и другие функции распределения памяти, может потерпите неудачу, вы должны учесть все возможные проблемы.
  • выделяйте ресурсы (в данном случае память) только тогда, когда вам это нужно.
  • вы должны уметь различать ошибки и действительные укусы.На данный момент вы не знаете, malloc() провал substr ("xxx",1,1) или рабочий substr ("xxx",1,0) создает пустую строку.
  • тебе не нужно calloc() память, которую вы все равно собираетесь перезаписать.
  • все недопустимые параметры должны либо вызывать ошибку, либо приводить к допустимому параметру (и ваш API должен документировать это).
  • вам не нужно устанавливать для локальной пустой строки значение NULL после ее освобождения — она будет потеряна при возврате функции.
  • вам не нужно использовать usr strncat() - ты должен узнайте размеры и доступную вам память, прежде чем выполнять любое копирование, чтобы вы могли использовать (скорее всего) быстрее memcpy().
  • вы используете базу 1, а не базу 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;
}
char* emptyString = strdup(""); /* C'mon! This cannot fail? */

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top