Это хорошая подстрока для C?
Вопрос
Смотрите также 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)
действительно полезно(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:Три строки кода не делают одно и то же, но в данном контексте все они дадут один и тот же результат.Спасибо, что поймали меня на этом.
Другие советы
Ваша функция кажется очень сложной для простой операции.Некоторые проблемы (не все из них являются ошибками):
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.