Как стандартным способом обрезать начальные/конечные пробелы?

StackOverflow https://stackoverflow.com/questions/122616

  •  02-07-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

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

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

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

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( str + len - 1 != endp )
            *(endp + 1) = '\0';
    else if( frontp != str &&  endp == frontp )
            *str = '\0';

    /* Shift the string so that it starts at str so that if it's 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;
}

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

int main(int argc, char *argv[])
{
    char *sample_strings[] =
    {
            "nothing to trim",
            "    trim the front",
            "trim the back     ",
            " trim one char front and back ",
            " trim one char front",
            "trim one char back ",
            "                   ",
            " ",
            "a",
            "",
            NULL
    };
    char test_buffer[64];
    int index;

    for( index = 0; sample_strings[index] != NULL; ++index )
    {
            strcpy( test_buffer, sample_strings[index] );
            printf("[%s] -> [%s]\n", sample_strings[index],
                                     trim(test_buffer));
    }

    /* The test prints the following:
    [nothing to trim] -> [nothing to trim]
    [    trim the front] -> [trim the front]
    [trim the back     ] -> [trim the 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]
    [] -> []
    */

    return 0;
}

Исходный файл был trip.c.Скомпилировано с помощью «cc Trim.c -o Trim».

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

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

Вот моя мини-библиотека 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); }

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

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

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.
}

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

Функции:
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 desire to 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);
  }

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

Вот решение, похожее на процедуру модификации на месте @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();

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

#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);
}

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

  1. Возвращен другой указатель внутри строки исходного указателя (своего рода боль совмещать два разных указателя на одно и то же).
  2. Безвозмездно использовал такие вещи, как стрлен() которые предварительно повторяют всю строку.
  3. Используются непереносимые функции библиотеки, специфичные для ОС.
  4. Обратное сканирование.
  5. Использовано сравнение с ' ' вместо isspace() чтобы TAB/CR/LF сохранились.
  6. Пустая трата памяти с большими статическими буферами.
  7. Ненужные циклы с дорогостоящими функциями, такими как ссканф/спринтф.

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

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

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

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

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

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

#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);
}

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

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

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

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

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

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

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

Использовать строковая библиотека, например:

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

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

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

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

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

}

Выход

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

Нить:Состояние ДБС:Запуск DBS — инициализация конфигурации DBS

Состояние ДБС

Стартап ДБС

Стартап ДБС

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

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

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

Токенизация завершена.

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

Чтобы это продолжалось, есть еще один вариант с изменяемой строкой:

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

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

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

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

#include <stdio.h>

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

Хорошо, это мой взгляд на вопрос.Я считаю, что это наиболее краткое решение, которое изменяет строку на месте (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;
}
#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 помогает обрезать все пробелы.

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

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

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

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

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

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

    Michael Burr
    michael.burr@nth-element.com
    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's no problem writing
    //      another there).    
    *end = '\0';

    return s;
}

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

Большинство ответов на данный момент делают одно из следующего:

  1. Возврат в конце строки (т.е.найдите конец строки, а затем выполните поиск назад, пока не будет найден символ, не являющийся пробелом), или
  2. Вызов 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';
}

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

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
}

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

#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);
}

Что вы думаете об использовании функции 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 меньше, чем пространство, что означает, что управляющие Chars также будут обрезаны!

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

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

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';

Здесь я использую динамическое распределение памяти, чтобы обрезать входную строку до функции 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;
}

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

#include <ctype.h>
#include <string.h>
void trim_str(char *s)
{
    const size_t s_len = strlen(s);

    int i;
    for (i = 0; i < s_len; i++)
    {
        if (!isspace( (unsigned char) s[i] )) break;
    }

    if (i == s_len)
    {
        // s is an empty string or contains only space characters

        s[0] = '\0';
    }
    else
    {
        // s contains non-space characters

        const char *non_space_beginning = s + i;

        char *non_space_ending = s + s_len - 1;
        while ( isspace( (unsigned char) *non_space_ending ) ) non_space_ending--;

        size_t trimmed_s_len = non_space_ending - non_space_beginning + 1;

        if (s != non_space_beginning)
        {
            // Non-space characters exist in the beginning of s

            memmove(s, non_space_beginning, trimmed_s_len);
        }

        s[trimmed_s_len] = '\0';
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top