157


78

Cの文字列から先頭と末尾の空白を削除する、きれいで、できれば標準的な方法はありますか? 私は自分でロールバックしたいと思いますが、これは同じくらい一般的な解決策の共通の問題だと思います。

37 Answer


148


文字列を変更できる場合は、

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

//(isspace((unsigned char)* str))str; //先頭のスペースを切り捨てます。

if(* str == 0)//全スペース? strを返します。

//末尾のスペースをトリムend = str strlen(str) -  1; while(end> str

//新しいヌルターミネータ文字を書き込みますend [1] = '\ 0';

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トリムホワイトスペース(char * out、size_t len、const char * str){(len == 0)の場合、0を返す。

const char * end; size_t out_size;

//(isspace((unsigned char)* str))str; //先頭のスペースを切り捨てます。

if(* str == 0)//全スペース? {* out = 0; 1を返します。 }

//末尾のスペースをトリムend = str strlen(str) -  1; while(end> str

//出力サイズを最小化された文字列長とバッファサイズから1を引いた値に設定するout_size =(end  -  str)<len-1? (末尾 - 文字列):len-1;

//トリミングされた文字列をコピーし、nullターミネータを追加するmemcpy(out、str、out_size); out [out_size] = 0。

out_sizeを返します。 }


31


これは文字列をバッファの最初の位置に移動するものです。 動的に文字列を割り当てた場合でも、trim()が返すのと同じポインタ上で文字列を解放できるように、この動作が必要な場合があります。

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

if(str == NULL){NULLを返します。 if(str [0] == '\ 0'){strを返す。 }

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

/ *前後のポインタを移動して、最初の空白以外のアドレスを指定します。
     *  両端からの文字。 * / while(isspace((unsigned char)* frontp)){frontp; if(endp!= frontp){while(isspace((unsigned char)*( -  endp))

if(str len  -  1!= endp)*(endp 1)= '\ 0';そうでなければ(frontp!= str

/ *文字列をシフトして、文字列が動的に変化する場合は、文字列がstrで始まるようにします。
     *  割り当てられた、我々はまだ返されたポインタでそれを解放することができます。 再利用に注意してください
     *  endpの文字列バッファの先頭を意味するようになりました。 * / endp = str; if(frontp!= str){while(* frontp){* endp = * frontp; * endp = '\ 0'; }

strを返します。 }

正しさをテストします。

int main(int argc、char * argv []){char * sample_strings [] = {"何もトリミングしない"、 "前面をトリムする"、 "後ろをトリムする"、 "前と後ろをトリミングする"、 "1つトリミングする" char front "、" 1文字後ろにトリムする "、"、 ""、 "a"、 ""、NULL}; char test_buffer [64]; intインデックス。

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

/ *テストは次を印刷します:[何もトリミングしない]  - > [何もトリミングしない] [前面をトリミング]  - > [前面をトリミング] [背面をトリム]  - > [背面をトリム] [1文字前面をトリム]戻る]  - > [一文字前と後ろをトリミング] [一文字前をトリミング]  - > [一文字前をトリム] [一文字後ろをトリミング]  - > [一文字後ろをトリミング] []  - > [] []  - > [] [a]  - > [a] []  - > [] * /

0を返します。 }

ソースファイルはtrim.cです。 'cc trim.c -o trim’でコンパイルされています。


20


私の解決策 文字列は変更可能でなければなりません。 他の解決策よりも優れているのは、後でfree()する必要がある場合に備えて、古いポインタを使い続けることができるように非スペース部分を先頭に移動することです。

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

while(isspace(p [l  -  1]))p [ -  l] = 0。 while(* p

memmove(s、p、l 1); }

このバージョンは、文字列のコピーをその場で編集する代わりにstrndup()で作成します。 strndup()には_GNU_SOURCEが必要なので、malloc()とstrncpy()を使用して独自のstrndup()を作成する必要があるかもしれません。

char * trim(char * s){整数l = strlen(s);

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

strndup(s、l)を返します。 }


9


これが私のC miniライブラリです。左、右、両方、すべて、その場で別々にトリミングし、指定された文字セット(またはデフォルトでは空白)をトリミングします。

* 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

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

1つのメインルーチンがそれをすべて行います。 src == _dst_の場合は適切な位置にトリミングされます。それ以外の場合は `strcpy`ルーチンのように動作します。 文字列_delim_で指定された文字セット、またはnullの場合は空白文字を削除します。 それは(trのように)左、右、両方、そしてすべてをトリミングします。 それほど多くはありません、そしてそれは一度だけ文字列を繰り返します。 何人かの人々は右にトリムすることが左から始まると不平を言うかもしれません、しかしとにかく左から始まるstrlenは必要ではありません。 (何らかの方法で、正しいトリムのために文字列の最後に到達する必要があるため、作業を進めることもできます。)パイプライン処理やキャッシュサイズなどについては、議論の余地があります。 。 このソリューションは左から右へと動作し、1回だけ繰り返すので、ストリームでも動作するように拡張できます。 制限事項:* Unicode 文字列では*動作しません


7


ここで私は単純で正しいインプレーストリム機能を試みました。

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

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

while((end> = begin)

//すべての文字を文字列配列の先頭に戻します。 for(i = begin; i <= end; i)str [i  -  begin] = str [i];

str [i  -  begin] = '\ 0'; //ヌル終了文字列。 }


5


トリムパーティーに遅刻

特徴:1。 他の多くの答えのように、始めを素早く切り取ってください。 + 2. 最後まで進んだ後は、ループごとに1つのテストで右側をトリミングします。 @ jfm3と同じですが、すべて空白文字の文字列に対して機能します3)。 char`が符号付き char`であるときの未定義の振る舞いを避けるためには、 * s`を unsigned char`にキャストしてください。

_ 文字処理「すべての場合、引数は「int」であり、その値は「unsigned char」として表現できるか、マクロ「EOF」の値と等しくなります。 引数に他の値がある場合、動作は未定義です。」C11§7.41 _

#include

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


4


もう1つ、1行で実際の仕事をします。

#含める

int main(){const char * target = "はは"; char buf [256]; sscanf(target、 "%s"、buf); //両側のトリミングはここで発生しますprintf( "<%s> \ n"、buf); }


3


私はこれらの答えのほとんどが好きではありませんでした。

  1. 元のポインタの文字列内に別のポインタを返しました(2つの異なるポインタを同じものにするのは面倒です)。

  2. * strlen()*のように文字列全体を前処理することを不必要に使用していました。

  3. 移植性のないOS固有のlib関数を使用しました。

  4. バックスキャン

  5. TAB / CR / LFが保持されるように、* isspace()の代わりに '' *との比較を使用しました。

  6. 大きなスタティックバッファを使用してメモリを浪費する。

  7. * sscanf / sprintf *のような高コスト関数を使った無駄なサイクル。

これが私のバージョンです:

void fnStrTrimInPlace(char * szWrite){

const char * szWriteOrig = szWrite; char * szLastSpace = szWrite、* szRead = szWrite。 int bNotSpace;

//文字列をシフトし、最初のNON-SPACE CHAR、LEFTMOSTで開始中while(* szRead!= '\ 0'){

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

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

* szWrite = * szRead; szWrite;

//(bNotSpace)szLastSpace = szWrite; //最後の非空間へのポインタの追跡。 }

szRead; }

//最後のNON-SPACEの後に終了(またはNON-SPACEがない場合は開始)* szLastSpace = '\ 0'; }


3


これは@ adam-rosenfieldsのインプレース変更ルーチンに似た解決策ですが、必要以上にstrlen()を使わないでください。 @jkramerと同様に、文字列はバッファ内で左寄せされるため、同じポインタを解放できます。 memmoveを使用しないため、大きな文字列には最適ではありません。 @ jfm3が言及する/ - 演算子を含みます。 FCTXベースのユニットテストが含まれています。

#含める

void trim(char * const a){char * p = a、* q = a; while(isspace(* q))q; while(* q)* p = * q; * p = '\ 0'; while(p> a

/ * http://fctx.wildbearsoftware.com/ * / #include "fct.h"を参照してください。

FCT_BGN(){FCT_QTEST_BGN(トリム){{char s [] = "";トリムfct_chk_eq_str( ""、s); } //簡単{char s [] = "";トリムfct_chk_eq_str( ""、s); } //簡単{char s [] = "\ t";トリムfct_chk_eq_str( ""、s); } //簡単{char s [] = "a";トリムfct_chk_eq_str( "a"、s); } // NOP {char s [] = "abc";トリムfct_chk_eq_str( "abc"、s); } // NOP {char s [] = "a";トリムfct_chk_eq_str( "a"、s); } //先頭{char s [] = "a c";トリムfct_chk_eq_str( "a c"、s); } //先頭{char s [] = "a";トリムfct_chk_eq_str( "a"、s); } //末尾{char s [] = "a c";トリムfct_chk_eq_str( "a c"、s); } //末尾の{char s [] = "a";トリムfct_chk_eq_str( "a"、s); } {char s [] = "a c";トリムfct_chk_eq_str( "a c"、s); } // 両方

// Villemoesは、メモリを破損したエッジケースを指摘しました。 ありがとうございました。 // http://stackoverflow.com/questions/122616/#comment23332594_4505533 {char s [] = "a"; // s 2トリム(s 2)の前に空白を入れてバッファする。 //空白のみを含む ""トリム "" fct_chk_eq_str( ""、s 2); //トリムからの正しい結果を確実にするfct_chk_eq_str( "a"、s); //前のバッファが変異していないことを確認します}

// doukremtは、このテストケースを調査することを提案しましたが、//好ましくない特定の動作を示すものではありませんでした。 // http://stackoverflow.com/posts/comments/33571430 {char s [] = "foobar"; //空白のトリムを超えて移動しました。 // fct_chk_eq_str( "foobar"、s);をトリムします。 //先頭の文字列が正しい

//これは、アルゴリズムが生成するものです。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();


2


パーティーにとても遅く…​

バックトラッキングのないシングルパス前方スキャンソリューション。 ソース文字列内のすべての文字は[line-through] *一度*二度正確にテストされます。 (そのため、特にソース文字列の末尾にスペースがたくさんある場合は、他のほとんどの解決策よりも高速になるはずです。)

これには2つの解決策があります。1つはソース文字列を別の宛先文字列にコピーしてトリムすること、もう1つは元の文字列を所定の位置にトリムすることです。 両方の機能は同じコードを使用します。

(変更可能な)文字列はその場で移動されるので、元の文字列へのポインタは変更されません。

#include #include

char * trim2(char * d、const char * s){//(s == NULL || d == NULL)がNULLを返すかどうかを確認する。

//先頭の空白をスキップするconst unsigned char * p =(const unsigned char *)s; while(isspace(* p))p;

//文字列unsigned char * dst =(unsigned char *)d;をコピーします。 // dとsは、同じ符号なしcharにすることができます。* end = dst; while(* p!= '\ 0'){if(!isspace(* dst = * p))end = dst; }

//末尾のスペースを切り捨てる* end = '\ 0'; dを返す。 }

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