Лучший алгоритм для разбиения ведущих и конечных пробелов в C

Каков наилучший подход при снятии верхних и конечных пробелов в C?

    Вы можете сделать это полностью на месте.

    void stripLeadingAndTrailingSpaces(char* string){ assert(string); /* First remove leading spaces */ const char* firstNonSpace = string; while(*firstNonSpace != '\0' && isspace(*firstNonSpace)) { ++firstNonSpace; } size_t len = strlen(firstNonSpace)+1; memmove(string, firstNonSpace, len); /* Now remove trailing spaces */ char* endOfString = string + len; while(string < endOfString && isspace(*endOfString)) { --endOfString ; } *endOfString = '\0'; } 

    Вот как kernel ​​linux выполняет обрезку, называемую strstrip ():

     char *strstrip(char *s) { size_t size; char *end; size = strlen(s); if (!size) return s; end = s + size - 1; while (end >= s && isspace(*end)) end--; *(end + 1) = '\0'; while (*s && isspace(*s)) s++; return s; } 

    Его в основном лучше отформатированная и проверенная с ошибками версия, о которой говорил предыдущий плакат.

    Этот вопрос выглядит так, как будто это может быть вопрос о домашнем задании, поэтому я отвечу наклонно: найдите страницы руководства для isspace (3) и strlen (3) и используйте арифметику указателей. Кроме того, в зависимости от проблемы вам может потребоваться malloc (3), чтобы удержать место для результата.

    Не забывайте, что представление строки C включает в себя конечный 0 байт, часто записываемый ‘\ 0’, который не считается частью длины строки.

    Вот версия, использующая isspace:

     char * trim(char *c) { char * e = c + strlen(c) - 1; while(*c && isspace(*c)) c++; while(e > c && isspace(*e)) *e-- = '\0'; return c; } 
     char *strstrip(char *s) { char *end; while ( (*s) && isspace( *s)) s++; if(!( *s) ) return s; end = s; while( ! *end) end++; end--; while (end ! = s && isspace( *end)) end--; *(end + 1) = '\0'; return s; } 

    Это в основном более оптимизированный код (с точки зрения скорости и кодирования).

    Если нам нужно сохранить память, тогда,

     void strstrip(char *s) { char *start; char *end; start = s; while ( (*start) && isspace( *start)) start++; if(!( *start) ) { *s='\0'; return ; } end = start; while( ! *end) end++; end--; while (end ! = start && isspace( *end)) end--; *(end + 1) = '\0'; memmove(s, start, end-start+1); return; } 

    Вот более краткая и более безопасная версия первой функции lakshmanaraj:

     #include  char *mytrim(char *s) { if(s) { /* Don't forget to check for NULL! */ while(*s && isspace(*s)) ++s; if(*s) { register char *p = s; while(*p) ++p; do { --p; } while((p != s) && isspace(*p)); *(p + 1) = '\0'; } } return(s); } в #include  char *mytrim(char *s) { if(s) { /* Don't forget to check for NULL! */ while(*s && isspace(*s)) ++s; if(*s) { register char *p = s; while(*p) ++p; do { --p; } while((p != s) && isspace(*p)); *(p + 1) = '\0'; } } return(s); } 

    Уточнение другого поста выше.

     void strstrip( char *s ) { char *start; char *end; // Exit if param is NULL pointer if (s == NULL) return; // Skip over leading whitespace start = s; while ((*start) && isspace(*start)) start++; // Is string just whitespace? if (!(*start)) { *s = 0x00; // Truncate entire string return; } // Find end of string end = start; while (*end) end++; // Step back from NUL end--; // Step backward until first non-whitespace while ((end != start) && isspace(*end)) end--; // Chop off trailing whitespace *(end + 1) = 0x00; // If had leading whitespace, then move entire string back to beginning if (s != start) memmove(s, start, end-start+1); return; } 

    Для тех, кто хотел бы найти рекурсивное решение, я предлагаю следующее:

     static char* trim_left_ptr(char* str) { if (*str == 0) { // no more in string. It is an empty string return str; } if (*str == ' ' || *str == '\t') { // it is space or tab. Try next. return trim_left_ptr(str + 1); } // found left side of string return str; } static char* trim_right_ptr(char* str) { if (*str == 0) { // found end of string return str; } // try next (recursion) char* ptr = trim_right_ptr( str + 1 ); // on the return from recursion. // ptr == str until a nonspace/nontab is found. if (*(ptr - 1) == ' ' || *(ptr - 1) == '\t') { // is space or tab. Have not yet found right side return ptr - 1; } // found right side of string return ptr; } char* trim(char* str) { char* L_Ptr = trim_left_ptr(str); char* R_Ptr = trim_right_ptr(str); // calculate characters to store in new buffer _int32 sz = R_Ptr - L_Ptr; // allocate buffer char* newstr = (char*) malloc(sz + 1); // copy trimmed string memcpy(newstr, L_Ptr, sz); // terminate string *(newstr + sz) = 0; return newstr; } 

    Конечно, это не единственное возможное рекурсивное решение.

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

    Вот пример кода с stripLeadingAndTrailingSpaces() из ответа AlfaZulu в стенограмме (включая завершающие пробелы) – только с добавлением static чтобы она соответствовала моим предрассудкам. (Я использую параметры компилятора, которые предотвращают компиляцию кода, если нет либо прототипа функции, либо функция статическая). Существует также функция str_strip() которая является фиксированной версией функции AlfaZulu. Испытательный жгут ставит две функции в работу. Код предполагает достаточно POSIX-подобную среду, так что strdup() может выделять дублируемую копию строки.

    Обратите внимание, что имя str_strip() позволяет избежать конфликтов с зарезервированными именами для стандартной библиотеки C:

    7.31.13 Обработка строк

    1 Названия функций, начинающиеся с str , mem или wcs и строчной буквы, могут быть добавлены к объявлениям в заголовке .

     #include  #include  #include  #include  #include  /* Code verbatim from answer by AlfaZulu (except for static) — which has problems */ static void stripLeadingAndTrailingSpaces(char* string){ assert(string); /* First remove leading spaces */ const char* firstNonSpace = string; while(*firstNonSpace != '\0' && isspace(*firstNonSpace)) { ++firstNonSpace; } size_t len = strlen(firstNonSpace)+1; memmove(string, firstNonSpace, len); /* Now remove trailing spaces */ char* endOfString = string + len; while(string < endOfString && isspace(*endOfString)) { --endOfString ; } *endOfString = '\0'; } static void str_strip(char *string) { assert(string); //printf("-->> %s(): [%s]\n", __func__, string); /* First remove leading spaces */ const char *firstNonSpace = string; while (isspace((unsigned char)*firstNonSpace)) ++firstNonSpace; //printf("---- %s(): [%s]\n", __func__, firstNonSpace); size_t len = strlen(firstNonSpace) + 1; memmove(string, firstNonSpace, len); //printf("---- %s(): [%s]\n", __func__, string); /* Now remove trailing spaces */ char *endOfString = string + len - 1; //printf("---- %s(): EOS [%s]\n", __func__, endOfString); while (string < endOfString && isspace((unsigned char)endOfString[-1])) --endOfString; *endOfString = '\0'; //printf("<<-- %s(): [%s]\n", __func__, string); } static void chk_stripper(const char *str) { char *copy1 = strdup(str); printf("V1 Before: [%s]\n", copy1); stripLeadingAndTrailingSpaces(copy1); printf("V1 After: [%s]\n", copy1); free(copy1); fflush(stdout); char *copy2 = strdup(str); printf("V2 Before: [%s]\n", copy2); str_strip(copy2); printf("V2 After: [%s]\n", copy2); free(copy2); fflush(stdout); } int main(void) { char *str[] = { " \t ABC DEF \t ", " \t \t ", " ", "", }; enum { NUM_STR = sizeof(str) / sizeof(str[0]) }; for (int i = 0; i < NUM_STR; i++) chk_stripper(str[i]); return 0; } 

    Когда вы работаете под Valgrind, я получаю вывод:

     $ valgrind --suppressions=etc/suppressions-macos-10.12.5 -- ./slts59 ==26999== Memcheck, a memory error detector ==26999== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==26999== Using Valgrind-3.13.0.SVN and LibVEX; rerun with -h for copyright info ==26999== Command: ./slts59 ==26999== V1 Before: [ ABC DEF ] V1 After: [ABC DEF ] V2 Before: [ ABC DEF ] V2 After: [ABC DEF] V1 Before: [ ] V1 After: [] V2 Before: [ ] V2 After: [] V1 Before: [ ] V1 After: [] V2 Before: [ ] V2 After: [] ==26999== Invalid read of size 1 ==26999== at 0x100000B81: stripLeadingAndTrailingSpaces (slts59.c:28) ==26999== by 0x100000CB0: chk_stripper (slts59.c:67) ==26999== by 0x100000DA2: main (slts59.c:91) ==26999== Address 0x100b7df01 is 0 bytes after a block of size 1 alloc'd ==26999== at 0x100096861: malloc (vg_replace_malloc.c:302) ==26999== by 0x1002DC938: strdup (in /usr/lib/system/libsystem_c.dylib) ==26999== by 0x100000C88: chk_stripper (slts59.c:65) ==26999== by 0x100000DA2: main (slts59.c:91) ==26999== ==26999== Invalid write of size 1 ==26999== at 0x100000B96: stripLeadingAndTrailingSpaces (slts59.c:33) ==26999== by 0x100000CB0: chk_stripper (slts59.c:67) ==26999== by 0x100000DA2: main (slts59.c:91) ==26999== Address 0x100b7df01 is 0 bytes after a block of size 1 alloc'd ==26999== at 0x100096861: malloc (vg_replace_malloc.c:302) ==26999== by 0x1002DC938: strdup (in /usr/lib/system/libsystem_c.dylib) ==26999== by 0x100000C88: chk_stripper (slts59.c:65) ==26999== by 0x100000DA2: main (slts59.c:91) ==26999== V1 Before: [] V1 After: [] V2 Before: [] V2 After: [] ==26999== ==26999== HEAP SUMMARY: ==26999== in use at exit: 34,572 bytes in 162 blocks ==26999== total heap usage: 186 allocs, 24 frees, 40,826 bytes allocated ==26999== ==26999== LEAK SUMMARY: ==26999== definitely lost: 0 bytes in 0 blocks ==26999== indirectly lost: 0 bytes in 0 blocks ==26999== possibly lost: 0 bytes in 0 blocks ==26999== still reachable: 0 bytes in 0 blocks ==26999== suppressed: 34,572 bytes in 162 blocks ==26999== ==26999== For counts of detected and suppressed errors, rerun with: -v ==26999== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2) $ 

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

    Я заметил, что хотя он имеет несколько отличную семантику, функция strstrip() основе strstrip() из ответа Tuminoid также чиста - нет ошибок доступа к памяти и удаляет конечные пробелы (не перемещая часть строки между первой и последней непустые символы).

     int i = strlen(s) - 1; while (isspace(s[i])) s[i--] = '\0'; while (isspace(*s)) s++; 

    Это должно позаботиться о проблеме, пока вы не заботитесь о том, чтобы качать строку, как сумасшедшую, и если вы не заботитесь о утечке памяти!

    Вы должны быть в состоянии сделать это на месте; удаление пробелов никогда не может привести к росту строки. Возможно, вы сможете сделать это без предварительной проверки длины строки, но это может быть бесполезно «умным». Вы должны заглянуть в memmove() , в дополнение к тем, о которых упоминал @Norman Ramsey.

    Если вы работаете в Linux / Windows и подключаете библиотеку к вашей программе, вы можете использовать подпрограмму g_strstrip() .

     char *strip(char *string) { char *start = string; while(isblank(*start)) start++; int end = strlen(start); if(start != string) { memmove(string, start, end); string[end] = '\0'; } while(isblank(*(string+end-1))) end--; string[end] = '\0'; return string; } 

    Корректированный алгоритм из fpsgamer (также ISO C90 действителен):

     void trimWhitespace(char *string) { const char* firstNonSpace = string; char* endOfString; size_t len; if (string[0] == '\0') { return; } /* First remove leading spaces */ while(*firstNonSpace != '\0' && isspace(*firstNonSpace)) { ++firstNonSpace; } len = strlen(firstNonSpace) + 1; memmove(string, firstNonSpace, len); /* Now remove trailing spaces */ endOfString = string + len; while(string < endOfString && (isspace(*endOfString) || *endOfString == '\0')) { --endOfString ; } *(endOfString + 1) = '\0'; } 

    для конечных пробелов используйте strtok. установите разделитель = “”, и когда он запускается, он отбрасывает байт разделителя и возвращает char * в токен

     char *x; char *y = "somestring "; x = strtok(y," "); 

    результат x = указатель на “somestring” не “somestring”

    Изменить: обновил код на основе последней версии библиотеки zString .

    Этот код не использует никакие библиотеки, а только арифметические указатели и целые числа. Есть три функции: обрезка, левая отделка и правая отделка. (Я должен добавить все эти функции в библиотеку zString :))

    • char *zstring_trim(char *s) удаляет передние и конечные пробелы

    • char *zstring_ltrim(char *s) удаляет ведущие белые пробелы

    • char *zstring_ltrim(char *s) удаляет конечные пробелы

    Все эти функции изменяют исходную строку символов

     /* trim */ char *zstring_trim(char *str){ char *src=str; /* save the original pointer */ char *dst=str; /* result */ char c; int is_space=0; int in_word=0; /* word boundary logical check */ int index=0; /* index of the last non-space char*/ /* validate input */ if (!str) return str; while ((c=*src)){ is_space=0; if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' ') is_space=1; if(is_space == 0){ /* Found a word */ in_word = 1; *dst++ = *src++; /* make the assignment first * then increment */ } else if (is_space==1 && in_word==0) { /* Already going through a series of white-spaces */ in_word=0; ++src; } else if (is_space==1 && in_word==1) { /* End of a word, dont mind copy white spaces here */ in_word=0; *dst++ = *src++; index = (dst-str)-1; /* location of the last char */ } } /* terminate the string */ *(str+index)='\0'; return str; } /* right trim */ char *zstring_rtrim(char *str){ char *src=str; /* save the original pointer */ char *dst=str; /* result */ char c; int is_space=0; int index=0; /* index of the last non-space char */ /* validate input */ if (!str) return str; /* copy the string */ while(*src){ *dst++ = *src++; c = *src; if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' ') is_space=1; else is_space=0; if (is_space==0 && *src) index = (src-str)+1; } /* terminate the string */ *(str+index)='\0'; return str; } /* left trim */ char *zstring_ltrim(char *str){ char *src=str; /* save the original pointer */ char *dst=str; /* result */ char c; int index=0; /* index of the first non-space char */ /* validate input */ if (!str) return str; /* skip leading white-spaces */ while((c=*src)){ if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' '){ ++src; ++index; } else break; } /* copy rest of the string */ while(*src) *dst++ = *src++; /* terminate the string */ *(src-index)='\0'; return str; } в /* trim */ char *zstring_trim(char *str){ char *src=str; /* save the original pointer */ char *dst=str; /* result */ char c; int is_space=0; int in_word=0; /* word boundary logical check */ int index=0; /* index of the last non-space char*/ /* validate input */ if (!str) return str; while ((c=*src)){ is_space=0; if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' ') is_space=1; if(is_space == 0){ /* Found a word */ in_word = 1; *dst++ = *src++; /* make the assignment first * then increment */ } else if (is_space==1 && in_word==0) { /* Already going through a series of white-spaces */ in_word=0; ++src; } else if (is_space==1 && in_word==1) { /* End of a word, dont mind copy white spaces here */ in_word=0; *dst++ = *src++; index = (dst-str)-1; /* location of the last char */ } } /* terminate the string */ *(str+index)='\0'; return str; } /* right trim */ char *zstring_rtrim(char *str){ char *src=str; /* save the original pointer */ char *dst=str; /* result */ char c; int is_space=0; int index=0; /* index of the last non-space char */ /* validate input */ if (!str) return str; /* copy the string */ while(*src){ *dst++ = *src++; c = *src; if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' ') is_space=1; else is_space=0; if (is_space==0 && *src) index = (src-str)+1; } /* terminate the string */ *(str+index)='\0'; return str; } /* left trim */ char *zstring_ltrim(char *str){ char *src=str; /* save the original pointer */ char *dst=str; /* result */ char c; int index=0; /* index of the first non-space char */ /* validate input */ if (!str) return str; /* skip leading white-spaces */ while((c=*src)){ if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' '){ ++src; ++index; } else break; } /* copy rest of the string */ while(*src) *dst++ = *src++; /* terminate the string */ *(src-index)='\0'; return str; } в /* trim */ char *zstring_trim(char *str){ char *src=str; /* save the original pointer */ char *dst=str; /* result */ char c; int is_space=0; int in_word=0; /* word boundary logical check */ int index=0; /* index of the last non-space char*/ /* validate input */ if (!str) return str; while ((c=*src)){ is_space=0; if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' ') is_space=1; if(is_space == 0){ /* Found a word */ in_word = 1; *dst++ = *src++; /* make the assignment first * then increment */ } else if (is_space==1 && in_word==0) { /* Already going through a series of white-spaces */ in_word=0; ++src; } else if (is_space==1 && in_word==1) { /* End of a word, dont mind copy white spaces here */ in_word=0; *dst++ = *src++; index = (dst-str)-1; /* location of the last char */ } } /* terminate the string */ *(str+index)='\0'; return str; } /* right trim */ char *zstring_rtrim(char *str){ char *src=str; /* save the original pointer */ char *dst=str; /* result */ char c; int is_space=0; int index=0; /* index of the last non-space char */ /* validate input */ if (!str) return str; /* copy the string */ while(*src){ *dst++ = *src++; c = *src; if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' ') is_space=1; else is_space=0; if (is_space==0 && *src) index = (src-str)+1; } /* terminate the string */ *(str+index)='\0'; return str; } /* left trim */ char *zstring_ltrim(char *str){ char *src=str; /* save the original pointer */ char *dst=str; /* result */ char c; int index=0; /* index of the first non-space char */ /* validate input */ if (!str) return str; /* skip leading white-spaces */ while((c=*src)){ if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' '){ ++src; ++index; } else break; } /* copy rest of the string */ while(*src) *dst++ = *src++; /* terminate the string */ *(src-index)='\0'; return str; } в /* trim */ char *zstring_trim(char *str){ char *src=str; /* save the original pointer */ char *dst=str; /* result */ char c; int is_space=0; int in_word=0; /* word boundary logical check */ int index=0; /* index of the last non-space char*/ /* validate input */ if (!str) return str; while ((c=*src)){ is_space=0; if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' ') is_space=1; if(is_space == 0){ /* Found a word */ in_word = 1; *dst++ = *src++; /* make the assignment first * then increment */ } else if (is_space==1 && in_word==0) { /* Already going through a series of white-spaces */ in_word=0; ++src; } else if (is_space==1 && in_word==1) { /* End of a word, dont mind copy white spaces here */ in_word=0; *dst++ = *src++; index = (dst-str)-1; /* location of the last char */ } } /* terminate the string */ *(str+index)='\0'; return str; } /* right trim */ char *zstring_rtrim(char *str){ char *src=str; /* save the original pointer */ char *dst=str; /* result */ char c; int is_space=0; int index=0; /* index of the last non-space char */ /* validate input */ if (!str) return str; /* copy the string */ while(*src){ *dst++ = *src++; c = *src; if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' ') is_space=1; else is_space=0; if (is_space==0 && *src) index = (src-str)+1; } /* terminate the string */ *(str+index)='\0'; return str; } /* left trim */ char *zstring_ltrim(char *str){ char *src=str; /* save the original pointer */ char *dst=str; /* result */ char c; int index=0; /* index of the first non-space char */ /* validate input */ if (!str) return str; /* skip leading white-spaces */ while((c=*src)){ if (c=='\t' || c=='\v' || c=='\f' || c=='\n' || c=='\r' || c==' '){ ++src; ++index; } else break; } /* copy rest of the string */ while(*src) *dst++ = *src++; /* terminate the string */ *(src-index)='\0'; return str; }