Как отрегулировать ведущее/завершающее пробелы стандартным образом?

Существует ли чистый, предпочтительно стандартный метод обрезки ведущего и конечного пробелов из строки в C? Я бы бросил свой собственный, но я думаю, что это обычная проблема с одинаково распространенным решением.

Ответ 1

Если вы можете изменить строку:

// 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;
}

Ответ 2

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

char *trim(char *str)
{
    size_t len = 0;
    char *frontp = str;
    char *endp = NULL;

    if( str == NULL ) { return NULL; }
    if( str[0] == '\0' ) { return str; }

    len = strlen(str);
    endp = str + len;

    /* Move the front and back pointers to address the first non-whitespace
     * characters from each end.
     */
    while( isspace((unsigned char) *frontp) ) { ++frontp; }
    if( endp != frontp )
    {
        while( isspace((unsigned char) *(--endp)) && endp != frontp ) {}
    }

    if( frontp != str && endp == frontp )
            *str = '\0';
    else if( str + len - 1 != endp )
            *(endp + 1) = '\0';

    /* Shift the string so that it starts at str so that if it dynamically
     * allocated, we can still free it on the returned pointer.  Note the reuse
     * of endp to mean the front of the string buffer now.
     */
    endp = str;
    if( frontp != str )
    {
            while( *frontp ) { *endp++ = *frontp++; }
            *endp = '\0';
    }

    return str;
}

Проверка на правильность:

#include <stdio.h>
#include <string.h>
#include <ctype.h>

/* Paste function from above here. */

int main()
{
    /* The test prints the following:
    [nothing to trim] -> [nothing to trim]
    [    trim the front] -> [trim the front]
    [trim the back     ] -> [trim the back]
    [    trim front and back     ] -> [trim front and back]
    [ trim one char front and back ] -> [trim one char front and back]
    [ trim one char front] -> [trim one char front]
    [trim one char back ] -> [trim one char back]
    [                   ] -> []
    [ ] -> []
    [a] -> [a]
    [] -> []
    */

    char *sample_strings[] =
    {
            "nothing to trim",
            "    trim the front",
            "trim the back     ",
            "    trim front and back     ",
            " trim one char front and back ",
            " trim one char front",
            "trim one char back ",
            "                   ",
            " ",
            "a",
            "",
            NULL
    };
    char test_buffer[64];
    char comparison_buffer[64];
    size_t index, compare_pos;

    for( index = 0; sample_strings[index] != NULL; ++index )
    {
        // Fill buffer with known value to verify we do not write past the end of the string.
        memset( test_buffer, 0xCC, sizeof(test_buffer) );
        strcpy( test_buffer, sample_strings[index] );
        memcpy( comparison_buffer, test_buffer, sizeof(comparison_buffer));

        printf("[%s] -> [%s]\n", sample_strings[index],
                                 trim(test_buffer));

        for( compare_pos = strlen(comparison_buffer);
             compare_pos < sizeof(comparison_buffer);
             ++compare_pos )
        {
            if( test_buffer[compare_pos] != comparison_buffer[compare_pos] )
            {
                printf("Unexpected change to buffer @ index %u: %02x (expected %02x)\n",
                    compare_pos, (unsigned char) test_buffer[compare_pos], (unsigned char) comparison_buffer[compare_pos]);
            }
        }
    }

    return 0;
}

Исходный файл был trim.c. Скомпилировано с 'cc -Wall trim.c -o trim'.

Ответ 3

Мое решение. Строка должна быть изменчивой. Преимущество выше некоторых других решений, которые он перемещает не-пространственную часть в начало, чтобы вы могли продолжать использовать старый указатель, если вам нужно освободить() позже.

void trim(char * s) {
    char * p = s;
    int l = strlen(p);

    while(isspace(p[l - 1])) p[--l] = 0;
    while(* p && isspace(* p)) ++p, --l;

    memmove(s, p, l + 1);
}   

Эта версия создает копию строки с помощью strndup() вместо редактирования на месте. strndup() требует _GNU_SOURCE, поэтому, возможно, вам нужно создать собственный strndup() с помощью malloc() и strncpy().

char * trim(char * s) {
    int l = strlen(s);

    while(isspace(s[l - 1])) --l;
    while(* s && isspace(* s)) ++s, --l;

    return strndup(s, l);
}

Ответ 4

Здесь моя C-мини-библиотека для обрезки влево, вправо, в обе, все, на месте и отдельно, и обрезка набора заданных символов (или пробела по умолчанию).

содержимое strlib.h:

#ifndef STRLIB_H_
#define STRLIB_H_ 1
enum strtrim_mode_t {
    STRLIB_MODE_ALL       = 0, 
    STRLIB_MODE_RIGHT     = 0x01, 
    STRLIB_MODE_LEFT      = 0x02, 
    STRLIB_MODE_BOTH      = 0x03
};

char *strcpytrim(char *d, // destination
                 char *s, // source
                 int mode,
                 char *delim
                 );

char *strtriml(char *d, char *s);
char *strtrimr(char *d, char *s);
char *strtrim(char *d, char *s); 
char *strkill(char *d, char *s);

char *triml(char *s);
char *trimr(char *s);
char *trim(char *s);
char *kill(char *s);
#endif

содержимое файла strlib.c:

#include <strlib.h>

char *strcpytrim(char *d, // destination
                 char *s, // source
                 int mode,
                 char *delim
                 ) {
    char *o = d; // save orig
    char *e = 0; // end space ptr.
    char dtab[256] = {0};
    if (!s || !d) return 0;

    if (!delim) delim = " \t\n\f";
    while (*delim) 
        dtab[*delim++] = 1;

    while ( (*d = *s++) != 0 ) { 
        if (!dtab[0xFF & (unsigned int)*d]) { // Not a match char
            e = 0;       // Reset end pointer
        } else {
            if (!e) e = d;  // Found first match.

            if ( mode == STRLIB_MODE_ALL || ((mode != STRLIB_MODE_RIGHT) && (d == o)) ) 
                continue;
        }
        d++;
    }
    if (mode != STRLIB_MODE_LEFT && e) { // for everything but trim_left, delete trailing matches.
        *e = 0;
    }
    return o;
}

// perhaps these could be inlined in strlib.h
char *strtriml(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_LEFT, 0); }
char *strtrimr(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_RIGHT, 0); }
char *strtrim(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_BOTH, 0); }
char *strkill(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_ALL, 0); }

char *triml(char *s) { return strcpytrim(s, s, STRLIB_MODE_LEFT, 0); }
char *trimr(char *s) { return strcpytrim(s, s, STRLIB_MODE_RIGHT, 0); }
char *trim(char *s) { return strcpytrim(s, s, STRLIB_MODE_BOTH, 0); }
char *kill(char *s) { return strcpytrim(s, s, STRLIB_MODE_ALL, 0); }

Одна основная процедура делает все. Он обрезается, если src== dst, в противном случае, он работает как подпрограммы strcpy. Он обрезает набор символов, указанных в строке delim, или пробел, если значение null. Он выравнивает левый, правый, оба и все (например, tr). Это не так много, и он перебирает строку только один раз. Некоторые люди могут пожаловаться на то, что триммер справа начинается слева, однако нет необходимости в strlen, который начинается слева. (Так или иначе вам нужно дойти до конца строки для правильных обрезков, чтобы вы могли также выполнять эту работу, когда идете.) Могут быть аргументы в отношении конвейерной обработки и размеров кеша и тому подобное - кто знает, Поскольку решение работает слева направо и выполняет итерации только один раз, его можно расширить, чтобы работать и с потоками. Ограничения: он работает не в строках unicode.

Ответ 5

Вот моя попытка простой, но правильной функции внутренней отделки.

void trim(char *str)
{
    int i;
    int begin = 0;
    int end = strlen(str) - 1;

    while (isspace((unsigned char) str[begin]))
        begin++;

    while ((end >= begin) && isspace((unsigned char) str[end]))
        end--;

    // Shift all characters back to the start of the string array.
    for (i = begin; i <= end; i++)
        str[i - begin] = str[i];

    str[i - begin] = '\0'; // Null terminate string.
}

Ответ 6

До поздней вечеринки

Особенности:
 1. Обрежьте начало быстро, как и в ряде других ответов.
 2. Пройдя до конца, обрежьте вправо только с 1 тестом на петлю. Как @jfm3, но работает со всей строкой пробела)
 3. Чтобы избежать неопределенного поведения, когда char является подписанным char, приведите *s к unsigned char.

Обработка символов "Во всех случаях аргумент является int, значение которого должно быть представлено как unsigned char или должно равняться значению макроса EOF. Если аргумент имеет любое другое значение, поведение не определено. " C11 §7.4 1

#include <ctype.h>

// Return a pointer to the trimmed string
char *string_trim_inplace(char *s) {
  while (isspace((unsigned char) *s)) s++;
  if (*s) {
    char *p = s;
    while (*p) p++;
    while (isspace((unsigned char) *(--p)));
    p[1] = '\0';
  }

  // If desired, shift the trimmed string

  return s;
}

@chqrlie прокомментировал, что выше не сдвигает обрезанную строку. Для этого....

// Return a pointer to the (shifted) trimmed string
char *string_trim_inplace(char *s) {
  char *original = s;
  size_t len = 0;

  while (isspace((unsigned char) *s)) {
    s++;
  } 
  if (*s) {
    char *p = s;
    while (*p) p++;
    while (isspace((unsigned char) *(--p)));
    p[1] = '\0';
    // len = (size_t) (p - s);   // older errant code
    len = (size_t) (p - s + 1);  // Thanks to @theriver
  }

  return (s == original) ? s : memmove(original, s, len + 1);
}

Ответ 7

Здесь есть решение, аналогичное обычной процедуре модификации @adam-rosenfields, но без необходимости прибегая к strlen(). Подобно @jkramer, строка корректируется по левому краю в буфере, поэтому вы можете освободить тот же указатель. Не оптимален для больших строк, так как он не использует memmove. Включает операторы ++/-, которые упоминает @jfm3. FCTX - включенные модульные тесты.

#include <ctype.h>

void trim(char * const a)
{
    char *p = a, *q = a;
    while (isspace(*q))            ++q;
    while (*q)                     *p++ = *q++;
    *p = '\0';
    while (p > a && isspace(*--p)) *p = '\0';
}

/* See http://fctx.wildbearsoftware.com/ */
#include "fct.h"

FCT_BGN()
{
    FCT_QTEST_BGN(trim)
    {
        { char s[] = "";      trim(s); fct_chk_eq_str("",    s); } // Trivial
        { char s[] = "   ";   trim(s); fct_chk_eq_str("",    s); } // Trivial
        { char s[] = "\t";    trim(s); fct_chk_eq_str("",    s); } // Trivial
        { char s[] = "a";     trim(s); fct_chk_eq_str("a",   s); } // NOP
        { char s[] = "abc";   trim(s); fct_chk_eq_str("abc", s); } // NOP
        { char s[] = "  a";   trim(s); fct_chk_eq_str("a",   s); } // Leading
        { char s[] = "  a c"; trim(s); fct_chk_eq_str("a c", s); } // Leading
        { char s[] = "a  ";   trim(s); fct_chk_eq_str("a",   s); } // Trailing
        { char s[] = "a c  "; trim(s); fct_chk_eq_str("a c", s); } // Trailing
        { char s[] = " a ";   trim(s); fct_chk_eq_str("a",   s); } // Both
        { char s[] = " a c "; trim(s); fct_chk_eq_str("a c", s); } // Both

        // Villemoes pointed out an edge case that corrupted memory.  Thank you.
        // http://stackoverflow.com/questions/122616/#comment23332594_4505533
        {
          char s[] = "a     ";       // Buffer with whitespace before s + 2
          trim(s + 2);               // Trim "    " containing only whitespace
          fct_chk_eq_str("", s + 2); // Ensure correct result from the trim
          fct_chk_eq_str("a ", s);   // Ensure preceding buffer not mutated
        }

        // doukremt suggested I investigate this test case but
        // did not indicate the specific behavior that was objectionable.
        // http://stackoverflow.com/posts/comments/33571430
        {
          char s[] = "         foobar";  // Shifted across whitespace
          trim(s);                       // Trim
          fct_chk_eq_str("foobar", s);   // Leading string is correct

          // Here is what the algorithm produces:
          char r[16] = { 'f', 'o', 'o', 'b', 'a', 'r', '\0', ' ',                     
                         ' ', 'f', 'o', 'o', 'b', 'a', 'r', '\0'};
          fct_chk_eq_int(0, memcmp(s, r, sizeof(s)));
        }
    }
    FCT_QTEST_END();
}
FCT_END();

Ответ 8

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

#include <stdio.h>

int main()
{
   const char *target = "   haha   ";
   char buf[256];
   sscanf(target, "%s", buf); // Trimming on both sides occurs here
   printf("<%s>\n", buf);
}

Ответ 9

Мне не понравилось большинство этих ответов, потому что они сделали одно или несколько из следующих...

  • Возвращает другой указатель внутри исходной строки указателя (вид боли, чтобы жонглировать двумя разными указателями на одно и то же).
  • Сделано безвозмездное использование таких вещей, как strlen(), которые предварительно итерируют всю строку.
  • Используются непереносимые функции библиотеки, специфичные для ОС.
  • Backscanned.
  • Используется сравнение с '' вместо isspace(), чтобы сохранить TAB/CR/LF.
  • Отработанная память с большими статическими буферами.
  • Отработанные циклы с дорогостоящими функциями, такими как sscanf/sprintf.

Вот моя версия:

void fnStrTrimInPlace(char *szWrite) {

    const char *szWriteOrig = szWrite;
    char       *szLastSpace = szWrite, *szRead = szWrite;
    int        bNotSpace;

    // SHIFT STRING, STARTING AT FIRST NON-SPACE CHAR, LEFTMOST
    while( *szRead != '\0' ) {

        bNotSpace = !isspace((unsigned char)(*szRead));

        if( (szWrite != szWriteOrig) || bNotSpace ) {

            *szWrite = *szRead;
            szWrite++;

            // TRACK POINTER TO LAST NON-SPACE
            if( bNotSpace )
                szLastSpace = szWrite;
        }

        szRead++;
    }

    // TERMINATE AFTER LAST NON-SPACE (OR BEGINNING IF THERE WAS NO NON-SPACE)
    *szLastSpace = '\0';
}

Ответ 10

Очень поздно на вечеринку...

Однопроходное решение для сканирования вперед без возврата. Каждый символ в исходной строке проверяется ровно один раз дважды. (Так что это должно быть быстрее, чем большинство других решений здесь, особенно если в исходной строке много пробелов.)

Это включает в себя два решения: одно для копирования и обрезки исходной строки в другую строку назначения, а другое для обрезки исходной строки на месте. Обе функции используют один и тот же код.

(Изменяемая) строка перемещается на место, поэтому исходный указатель на нее остается неизменным.

#include <stddef.h>
#include <ctype.h>

char * trim2(char *d, const char *s)
{
    // Sanity checks
    if (s == NULL  ||  d == NULL)
        return NULL;

    // Skip leading spaces        
    const unsigned char * p = (const unsigned char *)s;
    while (isspace(*p))
        p++;

    // Copy the string
    unsigned char * dst = (unsigned char *)d;   // d and s can be the same
    unsigned char * end = dst;
    while (*p != '\0')
    {
        if (!isspace(*dst++ = *p++))
            end = dst;
    }

    // Truncate trailing spaces
    *end = '\0';
    return d;
}

char * trim(char *s)
{
    return trim2(s, s);
}

Ответ 11

Я не уверен, что вы считаете "безболезненным".

Строки

C довольно болезненны. Мы можем найти первую несимметричную позицию символа тривиально:

while (isspace(* p)) p++;

Мы можем найти последнюю позицию без пробелов с двумя подобными тривиальными движениями:

while (* q) q++;
do { q--; } while (isspace(* q));

(Я избавил вас от боли при использовании операторов * и ++ в то же время.)

Вопрос в том, что вы делаете с этим? Тип данных под рукой на самом деле не является большой надежной абстрактной String, о которой легко думать, но на самом деле это всего лишь массив байтов хранения. Не имея надежного типа данных, невозможно написать функцию, которая будет делать то же самое, что и функция PHperytonby chomp. Что бы вернуть такую ​​функцию в C?

Ответ 12

Используйте строчную библиотеку, например:

Ustr *s1 = USTR1(\7, " 12345 ");

ustr_sc_trim_cstr(&s1, " ");
assert(ustr_cmp_cstr_eq(s1, "12345"));

... как вы говорите, это "общая" проблема, да вам нужно включить #include или около того, и она не включена в libc, но не придумывайте собственное хакерское задание, сохраняющее случайные указатели и size_t таким образом только приводит к переполнению буфера.

Ответ 13

#include "stdafx.h"
#include "malloc.h"
#include "string.h"

int main(int argc, char* argv[])
{

  char *ptr = (char*)malloc(sizeof(char)*30);
  strcpy(ptr,"            Hel  lo    wo           rl   d G    eo rocks!!!    by shahil    sucks b i          g       tim           e");

  int i = 0, j = 0;

  while(ptr[j]!='\0')
  {

      if(ptr[j] == ' ' )
      {
          j++;
          ptr[i] = ptr[j];
      }
      else
      {
          i++;
          j++;
          ptr[i] = ptr[j];
      }
  }


  printf("\noutput-%s\n",ptr);
        return 0;
}

Ответ 14

s был настолько полезен, я хотел сказать, что был рад, что это сообщение было доступно и чтобы показать, что я смог сделать с примерами. Мне нужно было сделать токенизацию большей строки, а затем взять подстроку и найти последнюю - чтобы я мог удалить новую строку из вызова fgets(), а также удалить пробел с передней части этого токена - так что я мог бы легко сравнить его со статической строкой. Первый пример в вышеприведенной записи заставил меня туда, так что спасибо. Вот как я использовал образцы кода и полученный результат.

int _tmain(int argc, _TCHAR* argv[])
{
   FILE * fp;   // test file
   char currDBSStatstr[100] = {"/0"};
   char *beg;
   char *end;
   char *str1;
   char str[] = "Initializing DBS Configuration";
   fp = fopen("file2-1.txt","r");
   if (fp != NULL)
   {
      printf("File exists.\n");
      fgets(currDBSStatstr, sizeof(currDBSStatstr), fp);
   }
   else
   {
      printf("Error.\n");
      exit(2);
   }  
   //print string
   printf("String: %s\n", currDBSStatstr);
   //extract first string
   str1 = strtok(currDBSStatstr, ":-");
   //print first token
   printf("%s\n", str1);
   //get more tokens in sequence
   while(1)
   {
      //extract more tokens in sequence
      str1 = strtok(NULL, ":-");
      //check to see if done
      if (str1 == NULL)
      {
         printf("Tokenizing Done.\n");
         exit(0);
      }
      //print string after tokenizing Done
      printf("%s\n", str1);
      end = str1 + strlen(str1) - 1;
      while((end > str1) && (*end == '\n'))
      {
         end--;
         *(end+1) = 0;
         beg = str1;
         while(isspace(*str1))
            str1++;
      }
      printf("%s\n", str1);
      if (strcmp(str, str1) == 0)
         printf("Strings are equal.\n");
   }
   return 0;

}

Выход

Файл существует.

String: Состояние DBS: запуск DBS - инициализация конфигурации DBS

Состояние DBS

Запуск DBS

Запуск DBS

Инициализация конфигурации DBS

Инициализация конфигурации DBS

Строки равны.

Выполняется токенизация.

Ответ 15

Просто чтобы это увеличилось, еще один вариант с изменяемой строкой:

void trimString(char *string)
{
    size_t i = 0, j = strlen(string);
    while (j > 0 && isspace((unsigned char)string[j - 1])) string[--j] = '\0';
    while (isspace((unsigned char)string[i])) i++;
    if (i > 0) memmove(string, string + i, j - i + 1);
}

Ответ 16

Если вы используете glib, вы можете использовать g_strstrip

Ответ 17

Я знаю, что есть много ответов, но я отправляю свой ответ здесь, чтобы узнать, достаточно ли моего решения.

// Trims leading whitespace chars in left `str`, then copy at almost `n - 1` chars
// into the `out` buffer in which copying might stop when the first '\0' occurs, 
// and finally append '\0' to the position of the last non-trailing whitespace char.
// Reture the length the trimed string which '\0' is not count in like strlen().
size_t trim(char *out, size_t n, const char *str)
{
    // do nothing
    if(n == 0) return 0;    

    // ptr stop at the first non-leading space char
    while(isspace(*str)) str++;    

    if(*str == '\0') {
        out[0] = '\0';
        return 0;
    }    

    size_t i = 0;    

    // copy char to out until '\0' or i == n - 1
    for(i = 0; i < n - 1 && *str != '\0'; i++){
        out[i] = *str++;
    }    

    // deal with the trailing space
    while(isspace(out[--i]));    

    out[++i] = '\0';
    return i;
}

Ответ 18

Самый простой способ пропустить ведущие пробелы в строке, imho,

#include <stdio.h>

int main()
{
char *foo="     teststring      ";
char *bar;
sscanf(foo,"%s",bar);
printf("String is >%s<\n",bar);
    return 0;
}

Ответ 19

Хорошо, это мой взгляд на вопрос. Я считаю, что это наиболее краткое решение, которое изменяет строку на месте (free воля работает) и избегает любых UB. Для небольших строк это, вероятно, быстрее, чем решение с использованием memmove.

void stripWS_LT(char *str)
{
    char *a = str, *b = str;
    while (isspace((unsigned char)*a)) a++;
    while (*b = *a++)  b++;
    while (b > str && isspace((unsigned char)*--b)) *b = 0;
}

Ответ 20

#include <ctype.h>
#include <string.h>

char *trim_space(char *in)
{
    char *out = NULL;
    int len;
    if (in) {
        len = strlen(in);
        while(len && isspace(in[len - 1])) --len;
        while(len && *in && isspace(*in)) ++in, --len;
        if (len) {
            out = strndup(in, len);
        }
    }
    return out;
}

isspace помогает обрезать все пробелы.

  • Запустите первый цикл, чтобы проверить из последнего байта символ пробела и уменьшить переменную длины
  • Запустите второй цикл, чтобы проверить из первого байта символ пробела и уменьшить переменную длины и увеличить указатель на символ.
  • Наконец, если переменная длины больше 0, используйте strndup для создания нового строкового буфера, исключив пробелы.

Ответ 21

Его тривиально с библиотекой регулярных выражений, так как мы говорим о "чистом" C?

Ответ 22

Лично я брошу свой собственный. Вы можете использовать strtok, но вам нужно позаботиться об этом (особенно если вы удаляете ведущие символы), что вы знаете, что такое память.

Избавление от конечных пробелов легко и довольно безопасно, так как вы можете просто положить 0 в верхнюю часть последнего пространства, отсчитывая его с конца. Избавление от ведущих пробелов означает перемещение вещей вокруг. Если вы хотите сделать это на месте (возможно, разумно), вы можете просто переместить все обратно на одного персонажа, пока там не будет ведущего места. Или, чтобы быть более эффективным, вы могли бы найти индекс первого непространственного символа и сдвинуть все обратно на это число. Или вы можете просто использовать указатель на первый непространственный символ (но тогда вам нужно быть осторожным так же, как и с strtok).

Ответ 23

Я только включаю код, потому что код, опубликованный до сих пор, кажется субоптимальным (и у меня пока нет комментариев).

void inplace_trim(char* s)
{
    int start, end = strlen(s);
    for (start = 0; isspace(s[start]); ++start) {}
    if (s[start]) {
        while (end > 0 && isspace(s[end-1]))
            --end;
        memmove(s, &s[start], end - start);
    }
    s[end - start] = '\0';
}

char* copy_trim(const char* s)
{
    int start, end;
    for (start = 0; isspace(s[start]); ++start) {}
    for (end = strlen(s); end > 0 && isspace(s[end-1]); --end) {}
    return strndup(s + start, end - start);
}

strndup() является расширением GNU. Если у вас его нет или что-то подобное, сверните свой собственный. Например:

r = strdup(s + start);
r[end-start] = '\0';

Ответ 24

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

#include <ctype.h>
#include <string.h>

/*
    Public domain implementations of in-place string trim functions

    Michael Burr
    [email protected]
    2010
*/

char* ltrim(char* s) 
{
    char* newstart = s;

    while (isspace( *newstart)) {
        ++newstart;
    }

    // newstart points to first non-whitespace char (which might be '\0')
    memmove( s, newstart, strlen( newstart) + 1); // don't forget to move the '\0' terminator

    return s;
}


char* rtrim( char* s)
{
    char* end = s + strlen( s);

    // find the last non-whitespace character
    while ((end != s) && isspace( *(end-1))) {
            --end;
    }

    // at this point either (end == s) and s is either empty or all whitespace
    //      so it needs to be made empty, or
    //      end points just past the last non-whitespace character (it might point
    //      at the '\0' terminator, in which case there no problem writing
    //      another there).    
    *end = '\0';

    return s;
}

char*  trim( char* s)
{
    return rtrim( ltrim( s));
}

Ответ 25

Большинство ответов до сих пор выполняют одно из следующих действий:

  • Откат в конце строки (т.е. найти конец строки, а затем искать назад до тех пор, пока не будет найден не-пробельный символ) или
  • Сначала вызовите strlen(), сделав второй проход через всю строку.

Эта версия делает только один проход и не отступает. Следовательно, он может работать лучше, чем другие, хотя только в том случае, если обычно имеется сотни конечных пробелов (что необычно при обработке вывода SQL-запроса).

static char const WHITESPACE[] = " \t\n\r";

static void get_trim_bounds(char  const *s,
                            char const **firstWord,
                            char const **trailingSpace)
{
    char const *lastWord;
    *firstWord = lastWord = s + strspn(s, WHITESPACE);
    do
    {
        *trailingSpace = lastWord + strcspn(lastWord, WHITESPACE);
        lastWord = *trailingSpace + strspn(*trailingSpace, WHITESPACE);
    }
    while (*lastWord != '\0');
}

char *copy_trim(char const *s)
{
    char const *firstWord, *trailingSpace;
    char *result;
    size_t newLength;

    get_trim_bounds(s, &firstWord, &trailingSpace);
    newLength = trailingSpace - firstWord;

    result = malloc(newLength + 1);
    memcpy(result, firstWord, newLength);
    result[newLength] = '\0';
    return result;
}

void inplace_trim(char *s)
{
    char const *firstWord, *trailingSpace;
    size_t newLength;

    get_trim_bounds(s, &firstWord, &trailingSpace);
    newLength = trailingSpace - firstWord;

    memmove(s, firstWord, newLength);
    s[newLength] = '\0';
}

Ответ 26

Это кратчайшая возможная реализация, о которой я могу думать:

static const char *WhiteSpace=" \n\r\t";
char* trim(char *t)
{
    char *e=t+(t!=NULL?strlen(t):0);               // *e initially points to end of string
    if (t==NULL) return;
    do --e; while (strchr(WhiteSpace, *e) && e>=t);  // Find last char that is not \r\n\t
    *(++e)=0;                                      // Null-terminate
    e=t+strspn (t,WhiteSpace);                           // Find first char that is not \t
    return e>t?memmove(t,e,strlen(e)+1):t;                  // memmove string contents and terminator
}

Ответ 27

Эти функции изменят исходный буфер, поэтому, если динамически выделено, оригинал указатель может быть освобожден.

#include <string.h>

void rstrip(char *string)
{
  int l;
  if (!string)
    return;
  l = strlen(string) - 1;
  while (isspace(string[l]) && l >= 0)
    string[l--] = 0;
}

void lstrip(char *string)
{
  int i, l;
  if (!string)
    return;
  l = strlen(string);
  while (isspace(string[(i = 0)]))
    while(i++ < l)
      string[i-1] = string[i];
}

void strip(char *string)
{
  lstrip(string);
  rstrip(string);
}

Ответ 28

Что вы думаете об использовании функции StrTrim, определенной в заголовке Shlwapi.h.? Это прямолинейное определение, определяющее самостоятельно.
Подробности можно найти на:
 http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx

Если у вас есть char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
Это даст ausCaptain как "GeorgeBailey" not "GeorgeBailey ".

Ответ 29

Чтобы обрезать мои строки с обеих сторон, я использую oldie, но gooody;) Он может обрезать все с ascii меньше, чем пробел, что означает, что контрольные символы также будут обрезаны!

char *trimAll(char *strData)
{
  unsigned int L = strlen(strData);
  if(L > 0){ L--; }else{ return strData; }
  size_t S = 0, E = L;
  while((!(strData[S] > ' ') || !(strData[E] > ' ')) && (S >= 0) && (S <= L) && (E >= 0) && (E <= L))
  {
    if(strData[S] <= ' '){ S++; }
    if(strData[E] <= ' '){ E--; }
  }
  if(S == 0 && E == L){ return strData; } // Nothing to be done
  if((S >= 0) && (S <= L) && (E >= 0) && (E <= L)){
    L = E - S + 1;
    memmove(strData,&strData[S],L); strData[L] = '\0';
  }else{ strData[0] = '\0'; }
  return strData;
}

Ответ 30

Здесь я использую динамическое выделение памяти для обрезки входной строки до функции trimStr. Сначала мы выясним, сколько непустых символов существует во входной строке. Затем мы выделяем массив символов с таким размером и заботимся о нулевом символе в конце. Когда мы используем эту функцию, нам нужно освободить память внутри основной функции.

#include<stdio.h>
#include<stdlib.h>

char *trimStr(char *str){
char *tmp = str;
printf("input string %s\n",str);
int nc = 0;

while(*tmp!='\0'){
  if (*tmp != ' '){
  nc++;
 }
 tmp++;
}
printf("total nonempty characters are %d\n",nc);
char *trim = NULL;

trim = malloc(sizeof(char)*(nc+1));
if (trim == NULL) return NULL;
tmp = str;
int ne = 0;

while(*tmp!='\0'){
  if (*tmp != ' '){
     trim[ne] = *tmp;
   ne++;
 }
 tmp++;
}
trim[nc] = '\0';

printf("trimmed string is %s\n",trim);

return trim; 
 }


int main(void){

char str[] = " s ta ck ove r fl o w  ";

char *trim = trimStr(str);

if (trim != NULL )free(trim);

return 0;
}