문제

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

다른 팁

다음은 문자열을 버퍼의 첫 번째 위치로 이동시키는 것이 있습니다. 문자열을 동적으로 할당하면 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( 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;
}

소스 파일은 trim.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가 필요하므로 malloc () 및 strncpy ()로 자신의 strndup ()를 만들어야 할 수도 있습니다.

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

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

    return strndup(s, l);
}

다음은 왼쪽, 오른쪽, 오른쪽, 모두, 제자리 및 별도의 CINI 라이브러리를위한 내 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 루틴. 문자열에 지정된 문자 세트를 다듬습니다. 델리, 또는 널이면 공백. 그것은 왼쪽, 오른쪽, 둘 다 (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. 같은 것들을 무시 무시한 사용 strlen () 그것은 전체 문자열을 미리 사용합니다.
  3. 사용하지 않는 OS- 특이 적 LIB 기능을 사용했습니다.
  4. 후면.
  5. 사용 된 비교 ' ' 대신에 isspace () 따라서 탭 / CR / LF가 보존됩니다.
  6. 큰 정적 버퍼로 메모리를 낭비합니다.
  7. 고비용 함수와 같은 낭비 된 사이클 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';
}

파티에 아주 늦었습니다 ...

역 추적이없는 단일 패스 전방 스캔 솔루션. 소스 문자열의 모든 문자는 정확히 테스트됩니다 한 번 두 배. (따라서 소스 문자열에 후행 공간이 많은 경우 다른 대부분의 솔루션보다 빠르야합니다.)

여기에는 소스 문자열을 다른 대상 문자열로 복사하고 다듬는 두 가지 솔루션이 포함됩니다. 두 기능 모두 동일한 코드를 사용합니다.

(수정 가능한) 문자열은 위치에 이동되므로 원래 포인터는 변경되지 않습니다.

#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 's와 동일한 기능을 작성하는 것은 불가능합니다. chomp 기능. C에서 그러한 기능은 무엇입니까?

a 문자열 라이브러리, 예를 들어:

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

S는 매우 도움이 되었기 때문에이 게시물이 제공되어 기쁘고 예제로 할 수있는 일을 보여주기 위해 기뻐한다고 말하고 싶었습니다. 더 큰 문자열을 토큰 화 한 다음 하위 문자열을 가져 와서 마지막 문자열을 찾아서 fgets () 호출에서 Newline을 제거하고 그 토큰의 앞면에서 공백을 제거 할 수있었습니다. 정적 문자열과 쉽게 비교하십시오. 위의 게시물의 첫 번째 예가 저를 데려 왔습니다. 감사합니다. 코드 샘플과 내가 얻은 출력을 사용한 방법은 다음과 같습니다.

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 구성 초기화

문자열은 동일합니다.

토큰 화 작업.

사용하는 경우 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;
}

문자열에서 선행 공간을 건너 뛰는 가장 쉬운 방법은 IMHO입니다.

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

Header Shlwapi.h에 정의 된 strtrim 함수 사용에 대해 어떻게 생각하십니까? 스스로 정의하는 것은 곧바로 정의됩니다.
세부 사항은 다음에서 찾을 수 있습니다.
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx

당신이 가지고 있다면
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
이것은 줄 것입니다 ausCaptain ~처럼 "GeorgeBailey" ~ 아니다 "GeorgeBailey ".

양쪽에서 내 문자열을 다듬기 위해 나는 Oldie를 사용하지만 Goooody를 사용합니다.) 공간보다 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;
}

지금까지 게시 된 코드가 차선책처럼 보이기 때문에 코드 만 포함합니다 (아직 댓글을 달아야 할 담당자가 없습니다.)

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