Cのコンソールから行を読み取る方法
質問
Cコンソールプログラムの全行を読む最も簡単な方法は何ですか 入力されたテキストは可変長である可能性があり、その内容について推測することはできません。
解決
動的メモリ管理が必要で、 fgets
関数を使用して行を読み取ります。ただし、読み取った文字数を確認する方法はないようです。したがって、fgetcを使用します。
char * getline(void) {
char * line = malloc(100), * linep = line;
size_t lenmax = 100, len = lenmax;
int c;
if(line == NULL)
return NULL;
for(;;) {
c = fgetc(stdin);
if(c == EOF)
break;
if(--len == 0) {
len = lenmax;
char * linen = realloc(linep, lenmax *= 2);
if(linen == NULL) {
free(linep);
return NULL;
}
line = linen + (line - linep);
linep = linen;
}
if((*line++ = c) == '\n')
break;
}
*line = '\0';
return linep;
}
注:取得を使用しないでください!境界チェックを行わず、バッファをオーバーフローさせる可能性があります
他のヒント
GNU Cライブラリまたは別のPOSIX準拠のライブラリを使用している場合は、 getline()
にファイルストリーム用の stdin
を渡します。
静的割り当ての行を読み取るための非常に単純だが安全でない実装:
char line[1024];
scanf("%[^\n]", line);
バッファオーバーフローの可能性はないが、行全体を読み取らない可能性がある、より安全な実装は次のとおりです。
char line[1024];
scanf("%1023[^\n]", line);
変数を宣言するために指定された長さと、フォーマット文字列で指定された長さとの間の「差」ではありません。これは歴史的な遺物です。
バッファオーバーフローが発生せず、入力を切り捨てないようにするために、文字ごとに(getc())ループを使用する必要がある場合があります。
したがって、コマンド引数を探している場合は、ティムの答えを見てください。 コンソールから行を読みたいだけの場合:
#include <stdio.h>
int main()
{
char string [256];
printf ("Insert your full address: ");
gets (string);
printf ("Your address is: %s\n",string);
return 0;
}
はい、安全ではありません。バッファオーバーランを実行できます。ファイルの終わりをチェックせず、エンコードやその他の多くのものをサポートしていません。 実際、私はそれがこのようなことをしたかどうかさえ考えませんでした。 私はちょっとめちゃくちゃに同意します:) しかし...「Cのコンソールから行を読む方法」のような質問が表示された場合、上記のような100行のコードではなく、gets()のような単純なものが必要だと思います。 実際、実際にこれらの100行のコードを記述しようとすると、取得したものを選択した場合よりも多くの間違いを犯すことになります;)
getline
実行可能な例
この回答に言及しましたが、ここに例を示します。
POSIX 7 であり、メモリを割り当て、割り当てられたバッファをループ上でうまく再利用します。
ポインターnewbs、これを読んでください: getlineの最初の引数がポインター&quot; char **&quot;へのポインターである理由&quot; char *&quot;の代わりに?
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *line = NULL;
size_t len = 0;
ssize_t read = 0;
while (read != -1) {
puts("enter a line");
read = getline(&line, &len, stdin);
printf("line = %s", line);
printf("line length = %zu\n", read);
puts("");
}
free(line);
return 0;
}
glibcの実装
POSIXがありませんか?たぶんあなたは glibc 2.23の実装。
これは getdelim
に解決されます。これは、任意の行ターミネータを持つ getline
の単純なPOSIXスーパーセットです。
増加が必要なときはいつでも割り当てられたメモリを2倍にし、スレッドセーフに見えます。
マクロを拡張する必要がありますが、これ以上改善する可能性は低いです。
提案されているように、getchar()を使用して、行末またはEOFが返されるまでコンソールから読み取り、独自のバッファーを作成できます。適切な最大行サイズを設定できない場合、バッファが動的に増加する可能性があります。
Cのヌル終了文字列として行を取得する安全な方法としてfgetsを使用することもできます。
#include <stdio.h>
char line[1024]; /* Generously large value for most situations */
char *eof;
line[0] = '\0'; /* Ensure empty line if no input delivered */
line[sizeof(line)-1] = ~'\0'; /* Ensure no false-null at end of buffer */
eof = fgets(line, sizeof(line), stdin);
コンソール入力を使い果たした場合、または何らかの理由で操作が失敗した場合、eof == NULLが返され、ラインバッファーが変更されない可能性があります(最初の文字を「\ 0」に設定すると便利です)。 / p>
fgetsはline []を過剰に埋めることはなく、正常に戻ったときに最後に受け入れられた文字の後にnullが存在することを保証します。
行末に達した場合、終了する「\ 0」の前の文字は「\ n」になります。
終了する「\ 0」の前に終了する「\ n」がない場合、さらにデータがあるか、次のリクエストがファイルの終わりを報告する可能性があります。どちらがどちらであるかを判断するには、別のfgetsを実行する必要があります。 (この点で、getchar()を使用したループは簡単です。)
上記の(更新された)サンプルコードでは、fgetの成功後にline [sizeof(line)-1] == '\ 0'である場合、バッファが完全に満たされていることがわかります。そのポジションが「\ n」によって進められている場合、あなたは幸運だったとわかります。それ以外の場合は、stdinにさらにデータがあるか、ファイルの終わりが先にあります。 (バッファが完全にいっぱいになっていなくても、ファイルの終わりにいる可能性があり、現在の行の終わりに「\ n」がないこともあります。文字列をスキャンして、または、文字列の最後(バッファ内の最初の '\ 0')の前にある '\ n'を削除すると、最初にgetchar()を使用することを好みます。)
最初のチャンクとして読んだ量よりも多くの行が残っている場合に対処するために必要なことを行います。バッファを動的に拡張する例は、getcharまたはfgetsで動作するように作成できます。注意が必要ないくつかのトリッキーなケースがあります(バッファが拡張される前に前の入力を終了した '\ 0'の位置で次の入力の保存を開始することを思い出すなど)。
説明は可変長について述べているが、私のような多くの人々が、検索されたものと一致するタイトルでこの投稿に来ます。ほとんどの場合、長さは事前にわかっています。
長さを事前に知っている場合は、以下を試してください:
char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read
ソース: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
Cのコンソールから行を読み取る方法
-
独自の機能の構築は、 C のコンソールから行を読み取るための方法の1つです。
-
動的メモリ割り当て を使用して、行のすべての文字と
'\ 0'
文字を保持するのに必要な十分な量のメモリを割り当てます。 -
そしてここでは、ユーザーが
'\ n' を入力するまで
getchar()
関数を使用して、文字列の各文字を1つずつスキャンするループを使用しています。 code>またはEOF
文字//the function to read lines of variable length char* scan_line(char *line) { int ch; //as getchar() returns `int` if( (line = malloc(sizeof(char))) == NULL) //allocating memory { //checking if allocation was successful or not printf("unsuccessful allocation"); exit(1); } line[0]='\0'; for(int index = 0; ( (ch = getchar())!='\n' ) && (ch != EOF) ; index++) { if( (line = realloc(line, (index + 2)*sizeof(char))) == NULL ) { //checking if reallocation was successful or not printf("unsuccessful reallocation"); exit(1); } line[index] = (char) ch; //type casting `int` to `char` line[index + 1] = '\0'; //inserting null character at the end } return line; }
-
これで、次のように行全体を読むことができます:
char *line = NULL; line = scan_line(line);
scan_line()
関数を使用したサンプルプログラム:
#include <stdio.h>
#include <stdlib.h> //for dynamic allocation functions
char* scan_line(char *line)
{
..........
}
int main(void)
{
char *a = NULL;
a = scan_line(a); //function call to scan the line
printf("%s\n",a); //printing the scanned line
free(a); //don't forget to free the malloc'd pointer
}
サンプル入力:
Twinkle Twinkle little star.... in the sky!
サンプル出力:
<*>先ほど同じ問題に遭遇しましたが、これが私の解決策でした。それが役立つことを願っています。
/*
* Initial size of the read buffer
*/
#define DEFAULT_BUFFER 1024
/*
* Standard boolean type definition
*/
typedef enum{ false = 0, true = 1 }bool;
/*
* Flags errors in pointer returning functions
*/
bool has_err = false;
/*
* Reads the next line of text from file and returns it.
* The line must be free()d afterwards.
*
* This function will segfault on binary data.
*/
char *readLine(FILE *file){
char *buffer = NULL;
char *tmp_buf = NULL;
bool line_read = false;
int iteration = 0;
int offset = 0;
if(file == NULL){
fprintf(stderr, "readLine: NULL file pointer passed!\n");
has_err = true;
return NULL;
}
while(!line_read){
if((tmp_buf = malloc(DEFAULT_BUFFER)) == NULL){
fprintf(stderr, "readLine: Unable to allocate temporary buffer!\n");
if(buffer != NULL)
free(buffer);
has_err = true;
return NULL;
}
if(fgets(tmp_buf, DEFAULT_BUFFER, file) == NULL){
free(tmp_buf);
break;
}
if(tmp_buf[strlen(tmp_buf) - 1] == '\n') /* we have an end of line */
line_read = true;
offset = DEFAULT_BUFFER * (iteration + 1);
if((buffer = realloc(buffer, offset)) == NULL){
fprintf(stderr, "readLine: Unable to reallocate buffer!\n");
free(tmp_buf);
has_err = true;
return NULL;
}
offset = DEFAULT_BUFFER * iteration - iteration;
if(memcpy(buffer + offset, tmp_buf, DEFAULT_BUFFER) == NULL){
fprintf(stderr, "readLine: Cannot copy to buffer\n");
free(tmp_buf);
if(buffer != NULL)
free(buffer);
has_err = true;
return NULL;
}
free(tmp_buf);
iteration++;
}
return buffer;
}
BSDシステムおよびAndroidでは、 fgetln
:
#include <stdio.h>
char *
fgetln(FILE *stream, size_t *len);
そのように:
size_t line_len;
const char *line = fgetln(stdin, &line_len);
line
はnullで終了せず、最後に \ n
(またはプラットフォームが使用しているもの)を含みます。ストリームに対する次のI / O操作後に無効になります。
次のようなもの:
unsigned int getConsoleInput(char **pStrBfr) //pass in pointer to char pointer, returns size of buffer
{
char * strbfr;
int c;
unsigned int i;
i = 0;
strbfr = (char*)malloc(sizeof(char));
if(strbfr==NULL) goto error;
while( (c = getchar()) != '\n' && c != EOF )
{
strbfr[i] = (char)c;
i++;
strbfr = (void*)realloc((void*)strbfr,sizeof(char)*(i+1));
//on realloc error, NULL is returned but original buffer is unchanged
//NOTE: the buffer WILL NOT be NULL terminated since last
//chracter came from console
if(strbfr==NULL) goto error;
}
strbfr[i] = '\0';
*pStrBfr = strbfr; //successfully returns pointer to NULL terminated buffer
return i + 1;
error:
*pStrBfr = strbfr;
return i + 1;
}
この関数はあなたが望むことをする必要があります:
char* readLine( FILE* file )
{
char buffer[1024];
char* result = 0;
int length = 0;
while( !feof(file) )
{
fgets( buffer, sizeof(buffer), file );
int len = strlen(buffer);
buffer[len] = 0;
length += len;
char* tmp = (char*)malloc(length+1);
tmp[0] = 0;
if( result )
{
strcpy( tmp, result );
free( result );
result = tmp;
}
strcat( result, buffer );
if( strstr( buffer, "\n" ) break;
}
return result;
}
char* line = readLine( stdin );
/* Use it */
free( line );
これが役立つことを願っています。