Как стандартным способом обрезать начальные / конечные пробелы? - PullRequest
162 голосов
/ 23 сентября 2008

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

Ответы [ 37 ]

2 голосов
/ 06 июля 2018

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

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

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

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

#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);
}
1 голос
/ 05 августа 2011

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

Запуск DBS

Запуск DBS

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

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

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

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

1 голос
/ 04 декабря 2009
#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;
}
1 голос
/ 24 сентября 2008

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

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

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

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

1 голос
/ 23 сентября 2008

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

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

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

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

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

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

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

1 голос
/ 31 декабря 2018
#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 для создания нового строкового буфера, исключив пробелы.
1 голос
/ 09 августа 2017

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

// 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;
}
1 голос
/ 06 октября 2018

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

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

1 голос
/ 09 ноября 2015

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

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