コメントの質問に対する直接の回答
私の質問はまだ答えられていませんreturn 0
。
なぜなら:
- Windows で実行している場合、
- ファイルはバイナリ ファイルとして開かれ、
- 行末で単語を終了する文字は、LF ではなく CR です。
次に関数を呼び出すと、最初のループで LF が読み取られ、アルファベット順ではないため無視されます。
主な回答
簡潔に言うと、コードは改行を認識します — 少なくとも Linux では。
#include <stdio.h>
#include <ctype.h>
enum { MAX_WORD = 50 };
static
int getWord(FILE *in, char str[])
{
int ch;
int i = 0;
while (!isalpha(ch = getc(in)) && ch != EOF)
;
if (ch == EOF)
return -1;
str[i++] = tolower(ch);
while (isalpha(ch = fgetc(in)) && ch != EOF)
{
if (i < MAX_WORD)
str[i++] = tolower(ch);
}
if (ch == '\n')
return 0;
str[i] = '\0'; // Bug; should be before the if
return 1;
}
int main(void)
{
char buffer[MAX_WORD];
int rc;
while ((rc = getWord(stdin, buffer)) >= 0)
printf("Got: %d (%s)\n", rc, buffer);
return 0;
}
入力ファイルが与えられた場合:
blossom flower
bewilder confound confuse perplex
dwell live reside
プログラムは次の出力を生成します。
Got: 1 (blossom)
Got: 0 (flowerm)
Got: 1 (bewilder)
Got: 1 (confound)
Got: 1 (confuse)
Got: 0 (perplex)
Got: 1 (dwell)
Got: 1 (live)
Got: 0 (residex)
改行を読み取ったとき (0 が返されたとき)、単語内に文字が残ってしまい、現在の単語が前の単語よりも短いことに注意してください。行の最後の単語が前の単語よりも長く、スタックが十分に乱雑である場合、不適切な動作が発生する可能性があります。条件の前に null 終了を移動することで、このバグを修正できますif
。出力は次のとおりです。
Got: 1 (blossom)
Got: 0 (flower)
Got: 1 (bewilder)
Got: 1 (confound)
Got: 1 (confuse)
Got: 0 (perplex)
Got: 1 (dwell)
Got: 1 (live)
Got: 0 (reside)
Windows では、プログラムが a '\r'
(CRLF 行末の CR 部分) を読み取った場合、単語を終了する文字が'\r'
であり、次の関数呼び出しで最初のループが実行されたため、ゼロ リターンはスキップされることに注意してください。をスキップし'\n'
ます。
プラットフォーム (Unix と Windows) を示すと、質問が明確になり、回答がより迅速に得られることに注意してください。
DOS (Windows) 形式のファイルを作成data.dos
し、同じ (バグ修正済み) バイナリ (Ubuntu 14.04 派生物で実行) でそれを読み取ると、出力は次のようになります。
Got: 1 (blossom)
Got: 1 (flower)
Got: 1 (bewilder)
Got: 1 (confound)
Got: 1 (confuse)
Got: 1 (perplex)
Got: 1 (dwell)
Got: 1 (live)
Got: 1 (reside)
これは、「CR が単語を終了し、最初のループが改行をスキップする」シナリオに正確に対応します。戦略的な場所に印刷ステートメントを追加してデバッグすることもできます。
#include <stdio.h>
#include <ctype.h>
enum { MAX_WORD = 50 };
static
int getWord(FILE *in, char str[])
{
int ch;
int i = 0;
while (!isalpha(ch = getc(in)) && ch != EOF)
{
if (ch == '\n') printf("Got-1 '\\n'\n");
else if (ch == '\r') printf("Got-1 '\\r'\n");
else printf("Got-1 '%c'\n", ch);
}
if (ch == EOF)
return -1;
str[i++] = tolower(ch);
while (isalpha(ch = fgetc(in)) && ch != EOF)
{
if (i < MAX_WORD)
str[i++] = tolower(ch);
}
if (ch == '\n') printf("Got-2 '\\n'\n");
else if (ch == '\r') printf("Got-2 '\\r'\n");
else printf("Got-2 '%c'\n", ch);
str[i] = '\0';
if (ch == '\n')
return 0;
return 1;
}
int main(void)
{
char buffer[MAX_WORD];
int rc;
while ((rc = getWord(stdin, buffer)) >= 0)
printf("Got: %d (%s)\n", rc, buffer);
return 0;
}
Unix ファイルでは、出力は次のようになります。
Got-2 ' '
Got: 1 (blossom)
Got-2 '\n'
Got: 0 (flower)
Got-2 ' '
Got: 1 (bewilder)
Got-2 ' '
Got: 1 (confound)
Got-2 ' '
Got: 1 (confuse)
Got-2 '\n'
Got: 0 (perplex)
Got-2 ' '
Got: 1 (dwell)
Got-2 ' '
Got: 1 (live)
Got-2 '\n'
Got: 0 (reside)
そしてWindowsファイルで:
Got-2 ' '
Got: 1 (blossom)
Got-2 '\r'
Got: 1 (flower)
Got-1 '\n'
Got-2 ' '
Got: 1 (bewilder)
Got-2 ' '
Got: 1 (confound)
Got-2 ' '
Got: 1 (confuse)
Got-2 '\r'
Got: 1 (perplex)
Got-1 '\n'
Got-2 ' '
Got: 1 (dwell)
Got-2 ' '
Got: 1 (live)
Got-2 '\r'
Got: 1 (reside)
Got-1 '\n'
Unix/Linux は CRLF の組み合わせを特別に扱わないことに注意してください。これらは、入力ストリーム内の隣接する 2 つの文字にすぎません。