web-dev-qa-db-ja.com

CでEOFになるまで、標準入力を文字列変数に読み込む方法は?

stdinchar*変数に読み込もうとすると「バスエラー」が発生します。 stdinに来るもの全体を読み、それを最初に変数に入れてから、変数の作業を続けたいだけです。

私のコードは次のとおりです。

char* content;
char* c;
while( scanf( "%c", c)) {
 strcat( content, c);
}

fprintf( stdout, "Size: %d", strlen( content));

しかし、どういうわけか、cat test.txt | myappを呼び出すと、常に「バスエラー」が返されます。ここで、myappは上記のコンパイル済みコードです。

私の質問は、EOFが変数に入るまで、どのようにstdinを読み取るのですか?コードでわかるように、stdinを超える入力のサイズを出力したいだけです。ファイルtest.txtのサイズと同じである必要がある場合。

scanfを使用するだけで十分だと思いました。おそらく、stdinを読み取るためのバッファリングされた方法ですか?

9
NovumCoder

まず、初期化されていないポインタを渡します。つまり、scanfstrcatは所有していないメモリを書き込みます。次に、strcatは2つのヌル終了文字列を想定していますが、cは単なる文字です。これにより、所有していないメモリが再度読み取られます。実際の処理を行っていないため、scanfは必要ありません。最後に、一度に1文字ずつ読み取るのは不必要に遅くなります。これがソリューションの始まりであり、最後の文字列にサイズ変更可能なバッファーを使用し、fgets呼び出しに固定バッファーを使用します

#define BUF_SIZE 1024
char buffer[BUF_SIZE];
size_t contentSize = 1; // includes NULL
/* Preallocate space.  We could just allocate one char here, 
but that wouldn't be efficient. */
char *content = malloc(sizeof(char) * BUF_SIZE);
if(content == NULL)
{
    perror("Failed to allocate content");
    exit(1);
}
content[0] = '\0'; // make null-terminated
while(fgets(buffer, BUF_SIZE, stdin))
{
    char *old = content;
    contentSize += strlen(buffer);
    content = realloc(content, contentSize);
    if(content == NULL)
    {
        perror("Failed to reallocate content");
        free(old);
        exit(2);
    }
    strcat(content, buffer);
}

if(ferror(stdin))
{
    free(content);
    perror("Error reading from stdin.");
    exit(3);
}

編集:Wolferがほのめかしたように、入力にNULLがあると、fgetsを使用するときに文字列が途中で終了します。 getline は、メモリ割り当てを処理し、NUL入力に問題がないため、利用可能な場合はより適切な選択です。

18

問題は、ccontentを割り当てたことがないため、定義された場所を指していないことです。割り当てられていないメモリや存在しないものを指している可能性があります。まったく。そして、あなたはそれらにデータを入れています。最初にそれらを割り当てる必要があります。 (これがバスエラーの通常の意味です。無効なメモリアクセスを実行しようとしました。)

(または、cは常に1文字しか保持しないため、char cとして宣言し、&cをscanfに渡すことができます。文字列を宣言する必要はありません。 。)

これを行うと、contentがすべての入力を保持するのに十分な長さであることを確認するという問題が発生します。予想される入力の量を推測し、少なくともその長さで割り当てる必要があります(そして、それを超えるとエラーになります)。または、十分な長さがない場合は、より大きなサイズで再割り当てする戦略が必要です。

ああ、そしてstrcatが単一の文字ではなく文字列を期待するという問題にも遭遇するでしょう。 cchar*のままにしても、scanf呼び出しはそれを文字列にしません。 1文字の文字列は、(メモリ内で)文字列の終わりを示すヌル文字が後に続く文字です。 scanfは、単一の文字をスキャンする場合、その後にヌル文字を挿入しません。その結果、strcpyは文字列の終わりがどこにあるかを知ることができず、ヌル文字を探してメモリをさまよいます。

7
Brooks Moses

実際の内容は気にしないのに、なぜわざわざ文字列を作成するのですか? getchar()も使用します:

int    c;
size_t s = 0;

while ((c = getchar()) != EOF)
{
  s++;
}

printf("Size: %z\n", s);

このコードは、ファイルに'\0'文字が含まれている場合を正しく処理します。

7
Carl Norum

ここでの問題は、mallocを介してメモリが割り当てられていないポインタ変数を参照しているため、未定義のポインタでstrcatを使用すると、結果が未定義になることです。何かを指して、バスエラーになってしまいました!

これは、必要な修正コードになります。

 char * content = malloc(100 * sizeof(char)); 
 char c; 
 if(content!= NULL){
 content [0] = '\ 0'; // Davidに感謝します!
 while((c = getchar())!= EOF)
 {
 if(strlen(content)<100){
 strcat (content、c); 
 content [strlen(content)-1] = '\ 0'; 
} 
} 
} 
/*変数を使用した場合*/
 free(content); 

このコードは、メモリを管理するプログラマの責任を強調しています。mallocごとにfreeがあり、そうでない場合は、メモリリークが発生します。

編集:David Gelhar私のグリッチを指摘してくれてありがとう!修正を反映するために上記のコードを修正しました...もちろん実際の状況では、おそらく100の固定値をおそらく#defineに変更して、2倍にすることでバッファーを簡単に拡張できるようにすることができますreallocを介してメモリの量を超え、サイズに合わせてトリミングします。

1
t0mm13b

(MAXL-1文字より短い)文字列を取得し、ファイルを文字ごとに処理したくないと仮定して、次のようにしました。

#include <stdio.h>
#include <string.h>
#define MAXL 256

main(){
  char s[MAXL];
  s[0]=0;
  scanf("%s",s);
  while(strlen(s)>0){
    printf("Size of %s : %d\n",s,strlen(s));
    s[0]=0;
    scanf("%s",s);
  };
}
0
user3715859