1

文字列内の単語を区切り、各単語を別の行に出力するプログラムに取り組んでいます。私は出力にいくつかの問題を抱えています。例のために。

"This is a string"

版画

""this 
"is"  
"a" 
"string" 
"

それ以外の

"this"
"is"
"a" 
"string"

コード:

#include <string.h>
#include <stdio.h>

void wholestring(char S[]) {
   int i;
   for (i=0; i<strlen(S); ++i) {
      }
   return;
}

int main(){
    const int mysize = 100;
    char mystr[mysize];
    char *newstr;

    fgets(mystr, mysize, stdin);
    wholestring(mystr);

    newstr = strtok (mystr, " ");

    while (newstr != '\0'){
        printf ("\"%s\" \n", newstr);
        newstr = strtok ('\0', " ");
    }
    return 0;
}
4

4 に答える 4

3

Xagyg は、ソース文字列から引用符を削除することについて正しいです。引用符を削除するだけです。何が起こっているのかというと、最初のスペースの前にあるため、開始引用符が最初の単語とグループ化されているため、文字列から " を取得し、printf から " を取得します。「文字列」でも同じ問題があります。文字列の後の最初の " は、読み込んだファイルの " です。次に、読み込んだ文字列からカーソルを次の行に移動する改行を取得し、printf から " を取得します。

これを解決するには、元の文字列から引用符を削除するだけでなく、ソース ファイルの末尾に改行を入れないようにする必要があります (fgets では行を区切るために改行が必要なため、1 行のソース ファイルしか処理できません)。または、次のように読み取った後、文字列から改行文字を削除します。

int mystr_length = strlen(mystr);
for (int i = mystr_length-1; i >= 0 && (mystr[i] == '\r' || mystr[i] == '\n'); i--) {
    mystr[i]='\0';
}

また、newstr はポインタであるため、NUL ascii 文字「\0」ではなく、NULL ポインタ「NULL」と比較する必要があります。strtok への最初のパラメーターと同じです。NULではなくNULLが必要です。

最後の strtok 応答を取得している文字列の末尾にスペースがある可能性はありますか?

ファイルから文字列を読み取る代わりに、プログラムにハードコードされた文字列から始めます。

また、解析を開始する前に、読み取った文字列を出力してください。

最後に、「whole_string」関数には大きな問題があります。文字列内の各文字について、文字列の長さを再計算しているため、文字列内のすべての文字を調べる必要があります。非常に長い文字列の場合、非常に長い時間がかかることがあります。代わりに、ループの前に長さを変数にキャッシュし、それを使用します

int string_length = strlen(S);
for (i=0; i<string_length; ++i) {

また、この質問で述べたように、その関数は実際には何もしませんが、実際には文字列を介して非効率的に何もしません。

以前はこれを一番上に置いていましたが、すべて間違っています。改行データをキャプチャしてから印刷しています。引用が表示され、カーソルが同じ行の先頭に送られ、その上に別の引用が表示されるため、最後の行に 1 つしか表示されません。

于 2013-05-19T05:30:01.443 に答える
1

mystrトークナイザーの使用を開始する前に、 の最初と最後の二重引用符を削除してください ( strtok)。または、引用符なしでコピーをトークン化します。

于 2013-05-19T05:55:47.963 に答える
1

正しい出力を生成するコードの 2 つのマイナー バリアント。

バリアント A

#include <string.h>
#include <stdio.h>

int main(void)
{
    char line[4096];

    if (fgets(line, sizeof(line), stdin) != 0)
    {
        static const char delims[] = " \"\n";
        char *token = strtok(line, delims);

        while (token != NULL)
        {
            printf("\"%s\"\n", token);
            token = strtok(NULL, delims);
        }
    }
    return 0;
}

これにより、可変長配列の使用が回避されます。C では、const int mysize = 100;とはコンパイル時の定数式ではないchar mystr[mysize];ため、VLA を作成します。mysizeC++ は通常の配列を作成します。違いはほとんど重要ではありませんが、C99 コンパイラ (または C++ コンパイラ) を使用していることがわかります。

ただし、定数には何のメリットもありませmysizeん。sizeof(mystr)の呼び出しで使用する必要がありfgets()mysize一度しか参照されないため、定数に置き換えることもできます。また、ブックマークファイル以外のものが使用される可能性はほとんどないため、入力の 1 行に 4096 を習慣的に使用しています。それよりも長い単一行になります。

変数を使用delimsすると、文字列は繰り返されません。区切り文字が変更された場合、変更する行は 1 行だけです。

変数の名前も変更しました。'my' プレフィックスは常に 'baby talk' のように見え、私のコードには決して現れません。

このコードは、 への呼び出しで EOF またはその他の I/O エラーを正しく処理することに注意してくださいfgets()。すべての入力関数の戻りステータスをチェックする習慣を身につけるのに早すぎることはありません! のような出力関数をチェックするとき、私は他の人たちと同じくらい怠け者ですprintf()が、入力関数は本当に重要です。

また、出力行の末尾の空白も削除しました。それらは本当に私を悩ませます—コードの末尾の空白はどこでもそうです.

また、'\0'はヌル ポインター定数ですが、従来の書き方ではなく、(マイナーな) 混乱を招き、プログラマーが を誤用することで怒りを覚えることにも注意してください'\0'。null ポインターにはNULLorを使用します。0特に文字に使用'\0'します。

バリアント B

バリアント A のコードには明らかな繰り返しがあります。関数には 2 つの呼び出しがありますが、strtok()呼び出しが 1 つだけになるようにコードを記述して、次のようにします。

#include <string.h>
#include <stdio.h>

int main(void)
{
    char line[4096];

    if (fgets(line, sizeof(line), stdin) != 0)
    {
        char *token;
        for (char *source = line; (token = strtok(source, " \n\"")) != NULL; source = NULL)
            printf("\"%s\"\n", token);
    }
    return 0;
}

nowへの呼び出しは 1 つstrtok()しかないため、区切り文字への参照も 1 つしかないため、再びリテラル文字列にすることができます。主forに変数名が長いため、ループ行が少し長くなっています (91 文字)。srcand (および NULL の場合は 0) を使用するとtok、読みやすさを大幅に損なうことなく、80 文字未満に減らすことができます。

どちらのバリアントも入力行をトークン化します。

"this is a string"

出力に:

"this"
"is"
"a"
"string"
于 2013-05-19T16:23:47.973 に答える