Если вы можете изменить строку:
// Note: This function returns a pointer to a substring of the original string.
// If the given string was allocated dynamically, the caller must not overwrite
// that pointer with the returned value, since the original pointer must be
// deallocated using the same allocator with which it was allocated. The return
// value must NOT be deallocated using free() etc.
char *trimwhitespace(char *str)
{
char *end;
// Trim leading space
while(isspace((unsigned char)*str)) str++;
if(*str == 0) // All spaces?
return str;
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) end--;
// Write new null terminator character
end[1] = '\0';
return str;
}
Если вы не можете изменить строку, то вы можете использовать в основном тот же метод:
// Stores the trimmed input string into the given output buffer, which must be
// large enough to store the result. If it is too small, the output is
// truncated.
size_t trimwhitespace(char *out, size_t len, const char *str)
{
if(len == 0)
return 0;
const char *end;
size_t out_size;
// Trim leading space
while(isspace((unsigned char)*str)) str++;
if(*str == 0) // All spaces?
{
*out = 0;
return 1;
}
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) end--;
end++;
// Set output size to minimum of trimmed string length and buffer size minus 1
out_size = (end - str) < len-1 ? (end - str) : len-1;
// Copy trimmed string and add null terminator
memcpy(out, str, out_size);
out[out_size] = 0;
return out_size;
}
str
- локальная переменная, и ее изменение не изменяет исходный указатель, который передается. Вызовы функций в C всегда передаются по значению, а не по ссылке.free()
функции. Скорее наоборот - я разработал это, чтобы избежать необходимости выделения памяти для эффективности. Если переданный адрес был распределен динамически, то вызывающая сторона по-прежнему отвечает за освобождение этой памяти, и вызывающая сторона должна быть уверена, что не перезаписывает это значение значением, возвращаемым здесь.isspace
tounsigned char
, в противном случае вы вызываете неопределенное поведение.Вот тот, который сдвигает строку в первую позицию вашего буфера. Возможно, вам понадобится такое поведение, чтобы при динамическом размещении строки вы могли освобождать ее по тому же указателю, который возвращает trim ():
Тест на правильность:
Исходный файл был trim.c. Скомпилировано с 'cc -Wall trim.c -o trim'.
источник
isspace
tounsigned char
, в противном случае вы вызываете неопределенное поведение.isspace()
так почему же существует разница между" "
и"\n"
? Я добавил модульные тесты для переноса строк и это выглядит КИ для меня ... ideone.com/bbVmqo*(endp + 1) = '\0';
. В примере теста ответа используется буфер 64, что позволяет избежать этой проблемы.Мое решение. Строка должна быть изменяемой. Преимущество перед некоторыми другими решениями заключается в том, что он перемещает непробельную часть в начало, поэтому вы можете продолжать использовать старый указатель на случай, если вам придется освободить () его позже.
Эта версия создает копию строки с помощью strndup (), а не редактирует ее на месте. strndup () требует _GNU_SOURCE, поэтому, возможно, вам нужно создать свой собственный strndup () с помощью malloc () и strncpy ().
источник
trim()
вызывает UB , еслиs
это""
как первыйisspace()
вызов будетisspace(p[-1])
иp[-1]
не обязательно ссылаться на юридическое местоположение.isspace
tounsigned char
, в противном случае вы вызываете неопределенное поведение.if(l==0)return;
чтобы избежать нулевой длины strВот моя библиотека C mini для обрезки левого, правого, как, все, на месте и отдельно, и обрезать набор указанных символов (или пробел по умолчанию).
содержимое strlib.h:
содержимое strlib.c:
Одна основная рутина делает все это. Он обрезается на месте, если src == dst , в противном случае он работает как
strcpy
процедуры. Обрезает набор символов, указанных в строке delimили пробел, если ноль. Он обрезает левый, правый, оба и все (как tr). В этом нет ничего особенного, и он перебирает строку только один раз. Некоторые люди могут жаловаться, что обрезка справа начинается слева, однако, нет необходимости в strlen, который начинается в любом случае слева. (Так или иначе, вы должны добраться до конца строки для правильной обрезки, так что вы можете выполнять работу по ходу дела.) Могут быть аргументы о конвейерной обработке и размерах кэша и тому подобное - кто знает , Поскольку решение работает слева направо и повторяется только один раз, его можно расширить и на потоки. Ограничения: он не работает со строками Unicode .источник
dtab[*d]
не бросает*d
наunsigned int
перед ее использованием в качестве индекса массива. В системе с подписанным char это будет считывать,dtab[-127]
что приведет к ошибкам и, возможно, к аварийному завершению .dtab[*delim++]
потому чтоchar
значения индекса должны быть приведены кunsigned char
. Код предполагает 8-битныйchar
.delim
должен быть объявлен какconst char *
.dtab[0xFF & (unsigned int)*d]
понятнее какdtab[(unsigned char)*d]
. Код работает с строками в кодировке UTF-8, но не удаляет последовательности без ASCII.Вот моя попытка простой, но правильной функции обрезки на месте.
источник
while ((end >= begin) && isspace(str[end]))
предотвратить UB, когдаstr is
"". Prevents
str [-1] `.isspace
tounsigned char
, в противном случае вы вызываете неопределенное поведение.<ctype.h>
предназначены для работы с целочисленными значениями, которые представляют либоunsigned char
специальное значение, либоEOF
. См. Stackoverflow.com/q/7131026/225757 .До поздней вечеринки
Особенности:
1. Обрежьте начало быстро, как и в ряде других ответов.
2. Пройдя до конца, обрежьте вправо только с 1 тестом на петлю. Как @ jfm3, но работает в течение всего белого пространства строки)
3. Для того, чтобы избежать неопределенное поведение , когда
char
это знаковоеchar
, приведение*s
кunsigned char
.@chqrlie прокомментировал, что выше не сдвигает обрезанную строку. Для этого ....
источник
Вот решение, похожее на процедуру модификации на месте @ adam-rosenfields, но без необходимости прибегать к strlen (). Как и @jkramer, строка корректируется в пределах буфера, чтобы вы могли освободить тот же указатель. Не оптимально для больших строк, так как не использует memmove. Включает операторы ++ / -, о которых упоминает @ jfm3. Включены юнит-тесты на основе FCTX .
источник
Другой, с одной линией, выполняющей настоящую работу:
источник
%n
спецификатором преобразования, и, в конце концов, я просто боюсь сделать это вручную.Мне не понравились большинство из этих ответов, потому что они сделали одно или несколько из следующих действий ...
Вот моя версия:
источник
isspace
tounsigned char
, в противном случае вы вызываете неопределенное поведение.while (isspace((unsigned char) *szWrite)) szWrite++;
помешал бы этому. Код также копирует все оставшиеся пробелы.*szWrite = *szRead
тогда, когда указатели не равны, в этом случае пропускает записи, но затем мы добавили еще одну ветвь сравнения / сравнения. С современным CPU / MMU / BP я понятия не имею, будет ли эта проверка проигрышной или выигрышной. С более простыми процессорами и архитектурой памяти дешевле просто скопировать и пропустить сравнение.Я не уверен, что вы считаете "безболезненным".
Струны C довольно болезненны. Мы можем найти первую позицию непробельного символа тривиально:
Мы можем найти последнюю позицию непробельного символа с двумя похожими тривиальными ходами:
(Я избавлен вам боль с помощью
*
и++
операторов одновременно.)Вопрос сейчас в том, что вы делаете с этим? Тип данных, который мы имеем в виду, на самом деле не является большим здравым абстракцией,
String
о котором легко думать, а просто не больше, чем массив байтов хранения. Не имея надежного типа данных, невозможно написать функцию, которая будет выполнять те же функции, что иchomp
функция PHperytonby . Что бы могла вернуть такая функция в C?источник
do { q--; } ...
узнать*q != 0
.Очень поздно на вечеринку ...
Однопроходное решение для сканирования вперед без возврата. Каждый символ в исходной строке проверяется ровно
один раздважды. (Так что это должно быть быстрее, чем большинство других решений здесь, особенно если в исходной строке много пробелов.)Это включает два решения: одно для копирования и обрезки исходной строки в другую строку назначения, а другое для обрезки исходной строки на месте. Обе функции используют один и тот же код.
Строка (модифицируемая) перемещается на место, поэтому исходный указатель на нее остается неизменным.
источник
'\0'
и затем проверяется с помощьюisspace()
. Кажется расточительным проверять всех персонажейisspace()
. Откат от конца строки должен быть более эффективным для непатологических случаев.trim()
ХОРОШО. Угловой корпус:trim2(char *d, const char *s)
возникают проблемы приd,s
наложении иs < d
.trim()
вести себя в этом угловом случае ? Вы просите обрезать и скопировать строку в память, занятую самой строкой. В отличие отmemmove()
этого, это требует определения длины исходной строки перед выполнением самой обрезки, что требует дополнительного сканирования всей строки. Лучше написать другуюrtrim2()
функцию, которая знает, как скопировать источник в место назначения в обратном направлении, и, вероятно, принимает дополнительный аргумент длины строки источника.Используйте библиотеку строк , например:
... как вы говорите, это "распространенная" проблема, да, вам нужно включить #include или около того, и он не включен в libc, но не изобретайте свою собственную хакерскую работу, хранящую случайные указатели, а size_t таким образом приводит только к переполнение буфера.
источник
Если вы используете
glib
, то вы можете использовать g_strstripисточник
Просто, чтобы сохранить этот рост, еще один вариант с изменяемой строкой:
источник
strlen()
возвращает ,size_t
что может превысить диапазонint
. пробел не ограничен пробелом. Наконец, но самое важное: неопределенное поведение,strcpy(string, string + i * sizeof(char));
потому что массивы источника и назначения перекрываются. Используйтеmemmove()
вместоstrcpy()
.while (isspace((int)string[i])) string[i--] = '\0';
может зацикливаться за началом строки. Вы должны объединить этот цикл с предыдущими и последующими строками и написатьwhile (i > 0 && isspace((unsigned char)string[--i])) { string[i] = '\0'; } size_t end = i;
end
не указывало на завершающий нулевой байт, и у васend = ++i;
все еще была проблема со строками, содержащими все пробельные символы. Я только что исправил код.Я знаю, что есть много ответов, но я публикую свой ответ здесь, чтобы посмотреть, достаточно ли хорошее решение
источник
isspace(*str)
UB когда*str < 0
.size_t n
хорошо, но интерфейс никоим образом не информирует вызывающую сторону о том,n
что он слишком мал для полной обрезанной строки. Рассмотримtrim(out, 12, "delete data not")
Самый простой способ пропустить начальные пробелы в строке, imho,
источник
" foo bar "
.Хорошо, это мой взгляд на вопрос. Я считаю, что это наиболее краткое решение, которое изменяет строку на месте (
free
будет работать) и избегает любых UB. Для небольших строк это, вероятно, быстрее, чем решение с использованием memmove.источник
b > str
Тест требуется только один раз.*b = 0;
нужен только один раз.isspace
помогает обрезать все пробелы.strndup
для создания нового строкового буфера, исключив пробелы.источник
strndup()
не является частью стандарта C, а только Posix. Но так как это довольно легко реализовать, это не имеет большого значения.trim_space("")
возвращаетсяNULL
. Я ожидаю указатель на""
.int len;
должно бытьsize_t len;
.isspace(in[len - 1])
UB когдаin[len - 1] < 0
.while (isspace((unsigned char) *in) in++;
прежде чемlen = strlen(in);
будет более эффективным , чем позднееwhile(len && *in && isspace(*in)) ++in, --len;
Лично я бы сам себя катал. Вы можете использовать strtok, но вам нужно позаботиться об этом (особенно если вы удаляете ведущих символов), чтобы вы знали, что такое память.
Избавиться от конечных пробелов легко и довольно безопасно, так как вы можете просто поставить 0 поверх последнего пробела, считая от конца до конца. Избавление от пробелов означает движение вещей вокруг. Если вы хотите сделать это на месте (вероятно, разумно), вы можете просто перемещать все назад на один символ, пока не будет пробела. Или, чтобы быть более эффективным, вы можете найти индекс первого непробельного символа и сдвинуть все обратно на это число. Или вы можете просто использовать указатель на первый непробельный символ (но тогда вам нужно быть осторожным так же, как вы делаете с strtok).
источник
источник
Немного опаздываю к игре, но я переверну рутину. Они, вероятно, не самые эффективные, но я считаю, что они правильные и простые (с
rtrim()
учетом сложности):источник
char
аргумент к,isspace()
чтобы(unsigned char)
избежать неопределенного поведения потенциально отрицательных значений. Также избегайте перемещения строки, если в нейltrim()
нет необходимости.Большинство ответов до сих пор делают одно из следующего:
strlen()
Сначала позвоните , сделав второй проход через всю строку.Эта версия делает только один проход и не возвращается. Следовательно, он может работать лучше, чем другие, хотя только в том случае, если распространены сотни пробелов (что весьма обычно при работе с выводом запроса SQL).
источник
strspn()
иstrcspn()
в жесткой петле. Это очень неэффективно, и накладные расходы превзойдут недоказанное преимущество одного прямого прохода.strlen()
обычно расширяется встроенным очень эффективным кодом, а не реальной проблемой. Обрезка начала и конца строки будет намного быстрее, чем проверка каждого символа в строке на белизну, даже в особом случае строк с очень небольшим количеством или отсутствием небелых символов.Это кратчайшая возможная реализация, о которой я могу подумать:
источник
char *trim(char *s) { char *p = s, *e = s + strlen(s); while (e > s && isspace((unsigned char)e[-1])) { *--e = '\0'; } while (isspace((unsigned char)*p)) { p++; } if (p > s) { memmove(s, p, e + 1 - p); } return s; }
Эти функции изменят исходный буфер, поэтому при динамическом размещении исходный указатель может быть освобожден.
источник
rstrip()
вызывает неопределенное поведение для пустой строки.lstrip()
излишне медленно работает со строкой с длинной начальной частью пробельных символов.isspace()
не должен передаватьсяchar
аргумент, потому что он вызывает неопределенное поведение для отрицательных значений, отличных отEOF
.Что вы думаете об использовании функции StrTrim, определенной в заголовке Shlwapi.h.? Это прямолинейно, скорее, самоопределение.
Подробности можно найти по адресу :
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx
Если у вас есть
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
Это даст
ausCaptain
как"GeorgeBailey"
нет"GeorgeBailey "
.источник
Чтобы обрезать мои струны с обеих сторон, я использую oldie, но gooody;) Он может обрезать что угодно с ascii меньше пробела, что означает, что управляющие символы также будут обрезаны!
источник
size_t
вместоunsigned int
. Код имеет множество избыточных тестов и вызывает неопределенное поведение,strncpy(strData,&strData[S],L)
потому что массивы источника и назначения перекрываются. Используйтеmemmove()
вместоstrncpy()
.Я только включаю код, потому что код, размещенный до сих пор, кажется неоптимальным (и у меня пока нет представителя, чтобы комментировать.)
strndup()
является расширением GNU. Если у вас его нет или что-то подобное, сверните свое. Например:источник
isspace(0)
определено как ложное, вы можете упростить обе функции. Также переместитеmemmove()
внутрьif
блока.Здесь я использую динамическое выделение памяти для обрезки входной строки до функции trimStr. Сначала мы выясним, сколько непустых символов существует во входной строке. Затем мы выделяем массив символов с таким размером и заботимся о нулевом символе в конце. Когда мы используем эту функцию, нам нужно освободить память внутри основной функции.
источник
Вот как я это делаю. Он обрезает строку на месте, поэтому не беспокойтесь об освобождении возвращаемой строки или потере указателя на выделенную строку. Возможно, это не самый короткий ответ, но он должен быть понятен большинству читателей.
источник
источник