web-dev-qa-db-ja.com

C-scanf()でスペースを無視する

簡単な文字列取得を試みています。スペースを含めることができる入力(stdin)から文字列を書き込み、保存する必要がありますwithout単語間のスペース。

これまでのところ、すべて(スペースも)を節約するこの単純なコードを記述しましたが、scanf()でスペースを無視する方法がわかりません。

int main(){
    char str[10];
    scanf("%[^\n]s, str);
    printf("%s", str;
}

入力がI love C programming!の場合、出力はIloveCprogramming!になります。

文字を無視するために%*を使用しようとしましたが、成功しませんでした。

文字列が保存されてすべてのスペースが削除されると「再スキャン」できることも知っていますが、できるだけ効率的に取得する必要があります。スペースを削除するためにすべての文字列を再スキャンすると、計算時間が大幅に増加します(代わりにスキャンして無視するだけで、O(n)の複雑さがあります)

2

これまでのところ、すべて(スペースも)を節約するこの単純なコードを記述しましたが、scanf()でスペースを無視する方法がわかりません。

これは、ほとんどの新しいCプログラマが行うこととは反対の方向から行われています。ほとんどのタイプのフィールド、特に%sフィールドではデフォルトでスペースがスキップされるため、問題は通常scanfでスペースをスキップすることではありません。スペースは通常、フィールド区切り文字として認識されるため、先行スペースがスキップされるだけでなく、スペースもフィールド内で読み取られません。 %[フィールドを使用しているのは、このことを知っているからだと思います。

しかし、あなたはあなたのケーキを持って、それを食べることもできません。フィールドディレクティブ%[^\n]は、読み取られるデータが改行以外の文字の連続で構成されることを示しています。 scanfは、このような文字をすべて忠実に読み取り、指定した配列に転送します。フィールドの一部であると伝えた一部の文字を転送しないようにscanfに指示するオプションはありません。

scanfを引き続き使用する場合は、2つのオプションがあります。

  • データを読み取った後にスペースを削除する、または
  • スペースで区切られた部分を読み取り、別々のフィールドとして転送します。

別の答えは、前者の方法をすでに説明しています。後者の方法は次のとおりです。

int main(void) {
    int field_count;

    do {
        char str[80];
        char tail;

        field_count = scanf("%79[^ \n]%c", str, &tail));
        if (field_count == 0) {
            // No string was scanned this iteration: the first available char
            // was a space or newline.  Consume it, then proceed appropriately.
            field_count = scanf("%c", &tail);
            if (field_count != 1 || tail == '\n') {
                // newline, end-of-file, or error: break out of the loop
                break;
            } // else it's a space -- ignore it
        } else if (field_count > 0) {
            // A string was scanned; print it:
            printf("%s", str);

            if (field_count == 2) {
                // A trailing character was scanned, too; take appropriate action:
                if (tail == '\n') {
                    break;
                } else if (tail != ' ') {
                    putchar(tail);
                } // else it is a space; ignore it
            }
        } // else field_count == EOF
    } while (field_count != EOF);
}

注意事項

  • scanf%79[^ \n]ディレクティブの79文字(最大)のフィールド幅。フィールド幅がないと、配列の境界を超えてしまうという重大なリスクがあります(文字列ターミネーターを使用するには、フィールドよりも1文字以上長くする必要があります)。
  • [はフィールドタイプであり、修飾子ではありません。 sは、文字列も処理する別個のフィールドタイプですが、動作が異なります。ここではsフィールドは使用されません。
  • scanfの戻り値は、正常にスキャンされたフィールドの数を示します。これは、入力と形式の間で不一致が発生した場合、または入力の最後に達した場合に、形式文字列で記述されているよりも少ない場合があります。 I/Oエラーが発生します。これらの可能性を考慮に入れる必要があります。
  • 2番目のフィールド%cが実際にスキャンされた場合、スペースまたは改行に到達せずにフィールド幅が使い果たされたために、先行する文字列フィールドが終了したかどうかを確認できます。改行が見られたためです。これらのケースはそれぞれ異なる処理を必要とします。
  • scanfmostフィールドタイプの先頭の空白をスキップしますが、%[および%cフィールドは3つのうちの2つです例外。
  • このアプローチでは、特にスペース文字(' ')をスキップします。水平タブと垂直タブ、キャリッジリターン、フォームフィードなどの他の空白文字はスキップしません。このアプローチは、これらを処理するように変更することもできますが、説明するだけで十分です。
1
John Bollinger

ジョブに間違ったツールを使用しています。 getc を使用する必要があります

そして、次のようにします

int ch;
char str[10];

// Loop until either loop reaches 9 (need one for null character) or EOF is reached
for (int loop = 0; loop < 9 && (ch = getc(stdin)) != EOF; ) {
   if (ch != ' ' ) {
     str[loop] = ch;
     ++loop;
   }
}
str[loop] = 0;

printf("%s", str);

再スキャンは不要

4
Ed Heal

( ''に加えて)入力から他の空白を削除することに興味がある場合は、Cライブラリ関数isspace(。)を組み込むこともできます。次の標準的な空白文字の場合:

''(0x20)スペース(SPC)
'\ t'(0x09)水平タブ(TAB)
'\ n'(0x0a)改行(LF)
'\ v'(0x0b)垂直タブ(VT)
'\ f'(0x0c)フィード(FF)
'\ r'(0x0d)キャリッジリターン(CR)

この例では、isspace(.);ライブラリ関数を使用して関数を組み込み、C文字列からすべての標準の空白を消去するメソッドを提供します。

int main(void)
{
    char string[] = {"this contain's \n whitespace\t"};
    int len = strlen(string);
    char out[len+1];// +1 for null terminator 
                    //(accommodates when input contains no whitespace)
    int count = clean_whitespace(string, out);

    return 0;
}

int clean_whitespace(const char *in, char *out)
{
    int len, count=0, i;
    if((in) && (out))
    {
        len = strlen(in);
        for(i=0;i<len;i++)
        {
            if(!isspace(in[i]))
            {
                out[count++] = in[i];
            }
        }
        out[count]=0;//add null terminator.
    }
    return count;
}
2
ryyker

scanf()は目的に役立ちません。実際、入力の行からスペースを削除するためのバッファも必要ありません。一度に1バイトずつ読み取り、スペースを無視して、他のスペースを出力し、改行またはEOFで停止します:

#include <stdio.h>

int main(void) {
    int c;
    while ((c = getchar()) != EOF) {
        if (c != ' ') {
            putchar(c);
        }
        if (c == '\n') {
            break;
        }
    }
    return 0;
}

コードに問題があることにも注意してください:

  • scanf()フォーマット文字列が終了していません
  • 末尾のsは正しくありません。形式は%[^\n]です。
  • nullターミネータの前に配列に格納する最大バイト数を指定する方が安全です:scanf("%9[^\n]", str);
  • 空の行や空のファイルなどで変換が失敗した場合に、初期化されていない配列をprintfに渡さないようにするには、scanf()の戻り値をテストする必要があります。

scanf()を使用すると、空白を無視してchar c; while (scanf(" %c", &c) == 1) { putchar(c); }を非効率的に文字を読み取る非効率的な方法として使用できますが、行の終わりを検出できません。

これを投稿して、scanfだけでこの問題を解決することもできることを示しています。

 int main() {
   char a[10];
    for(int i = 0; i < 10 ; i++){
        scanf("%c", &a[i]);
        if( a[i] == ' ')
        i--;
    }
}

上記のものは、間にスペースを入れずに10文字をスキャンするだけです。

    for(int i = 0; i < 9; i++){
       printf("%c,", a[i]);
     }

    printf("%c", a[9]);

これは、スペースを別のものに置き換える場合に使用する方法です。例: '、'

入力にさらに多くの文字を含める場合は、新しい変数xを定義し、10をxに、9をx-1に変更します。