Cで慣用的なトリム関数を作成しようとしています。これはどのように見えますか?代わりに、新しい文字列をmallocして返す必要がありますか?
void trim(const char *input, char *result) { int i, j = 0; for (i = 0; input[i] != "\0"; i++) { if (!isspace(input[i])) { result[j++] = input[i]; } } }
コメント
- あります'そのコードには多くの問題があり、'バッファオーバーフロー攻撃に対して脆弱であり、'典型的な" trim "関数が行うことを実行します。トリムは、先頭と末尾の空白を削除します。これにより、それらがすべて削除されます。
- ありがとうございます。バッファオーバーフロー攻撃の処理方法について詳しく教えてください。
- 割り当てられているスペースの量がわからない場合は、データをバッファに盲目的にコピーしないでください。'それに、'はただトラブルを求めているだけです。簡単なことは、バッファのサイズを取り込むパラメータを追加することです。そうすれば、'がすべて発信者に表示され、実際の大きさがわかります。次に、'は、指定された長さを超えて読み取り/書き込みを試みないようにする必要があります。もちろん、'は絶対確実ではありません。発信者はあなたに偽の長さを与えることができますが、それはあなたではなく、彼らの側で問題になります。
回答
@JeffMercadoが指摘したように、これにより、先頭と末尾のスペースが削除されるのではなく、スペースが削除されます。現在の機能を維持したい場合は、remove_spaces
と呼びましょう。
ここに本当に微妙なバグがあります:
... isspace(input[i]) ...
isspace
は unsigned char またはEOF
。通常は署名されているchar
を渡すと、未定義の動作が発生します。代わりに、次のように言います。
... isspace((unsigned char) input[i]) ...
別のバグ:NULターミネーターを出力しないため、呼び出し元は文字列の長さを知る方法がありません(関数を呼び出す前にバッファをゼロにしない限り)。
これらのバグを修正すると、次のようになります。
void remove_spaces(const char *input, char *result) { int i, j = 0; for (i = 0; input[i] != "\0"; i++) { if (!isspace((unsigned char) input[i])) { result[j++] = input[i]; } } result[j] = "\0"; }
@JeffMercadoもこの関数について述べていますはバッファオーバーフローに対して脆弱です。ある意味では、呼び出し元が少なくともstrlen(input) + 1
のバッファを割り当てることを知っている場合、これは当てはまりません。しかし、呼び出し元は怠惰で、
。出力バッファサイズパラメータを追加すると、このような間違いを防ぐことができます。
void remove_spaces(const char *input, char *output, size_t output_size);
これを実装できるかどうかを確認してください。覚えておくべきいくつかの事柄:
-
出力バッファサイズをチェックするときは、NULターミネータを忘れないでください。
-
strncpy のようにしないでください。文字列を切り捨てる必要がある場合は、微妙なバグが発生する可能性があるため、NULターミネータを省略してください。
-
int
をi
およびj
に使用する場合output_size
のsize_t
では、符号付きと符号なしの比較に関するコンパイラ警告が表示されます。そうでない場合は、コンパイラの警告を表示します。コマンドラインからGCCを使用している場合は、gcc -Wall -W
と入力する習慣をつけてください。
コメント
-
strncpy()
は文字列関数ではありません一部の人は想定していますが。したがって、文字列になる結果はとにかく偶然になります。アナロジーはせいぜい大ざっぱなものになります。
回答
ポインタを前後に移動できることはわかっています。 、また、文字列を左からトリミングできることもわかっています。ポインタをインクリメントし、ポインタをデクリメントして右からトリミングする場合、2つのwhile
ループで十分です。右の歩行数が左の歩行数よりも少ないことに気付くでしょう。
右トリムコード:
#include <stdio.h> #include <ctype.h> void trim_both(char *, char *); int main (void) { char title[100] = " My long string "; char title_t[100] = ""; (void) printf("String before left trim is:[%s]\n", title); trim_both(title, title_t); (void) printf("String after left trim is:[%s]\n", title_t); } // trim spaces from left void trim_both(char *title_p, char *title_tp) { int flag = 0; // from left while(*title_p) { if(!isspace((unsigned char) *title_p) && flag == 0) { *title_tp++ = *title_p; flag = 1; } title_p++; if(flag == 1) { *title_tp++ = *title_p; } } // from right while(1) { title_tp--; if(!isspace((unsigned char) *title_tp) && flag == 0) { break; } flag = 0; *title_tp = "\0"; } }
回答
最も簡単な方法(スペースのみを削除):
Trim.Start:
- 文字を比較するまで文字列の先頭で
" "
(スペースまたは\n
や\t
などの他の文字)と等しいtemp(i
)変数をインクリメントします。 -
i
(str+=i
)。文字列はスペース文字(または他の白い文字)ではない文字から始まります。
Trim.End:
- Trim.Startについても同じことを行いますが、文字列の末尾から行います。
- 最後の文字(最後のスペース)を
\0
として設定します。
重要なことは、関数がポインタをポインタ(文字列)に変換することです。関数呼び出しに注意してください:StringTrim(&p2);
char * StringTrim(char * *pointerToString) { u8 start=0, length=0; // Trim.Start: length = strlen(*pointerToString); while ((*pointerToString)[start]==" ") start++; (*pointerToString) += start; if (start < length) // Required for empty (ex. " ") input { // Trim.End: u8 end = strlen(*pointerToString)-1; // Get string length again (after Trim.Start) while ((*pointerToString)[end]==" ") end--; (*pointerToString)[end+1] = 0; } return *pointerToString; }
使用法:
char str1[] = " test1 "; char * p1 = str1; Debug("1. before trim: [%s]", p1); StringTrim(&p1); Debug("1. after trim [%s]", p1); char str2[] = " test2"; char * p2 = str2; Debug("2. before trim: [%s]", p2); StringTrim(&p2); Debug("2. after trim [%s]", p2); char str3[] = "test3 "; char * p3 = str3; Debug("3. before trim: [%s]", p3); StringTrim(&p3); Debug("3. after trim [%s]", p3); char str4[] = " "; char * p4 = str4; Debug("4. before trim: [%s]", p4); StringTrim(&p4); Debug("4. after trim [%s]", p4); char str5[] = ""; char * p5 = str5; Debug("5. before trim: [%s]", p5); StringTrim(&p5); Debug("5. after trim [%s]", p5);
結果 :
1. before trim: [ test1 ] 1. after trim [test1] 2. before trim: [ test2] 2. after trim [test2] 3. before trim: [test3 ] 3. after trim [test3] 4. before trim: [ ] 4. after trim [] 5. before trim: [] 5. after trim []