回答:
動的メモリ管理が必要であり、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;
}
注:getを使用しないでください!境界チェックを行わず、バッファをオーバーフローさせる可能性があります
fgetc_unlocked
スレッドの安全性が問題ではないがパフォーマンスが問題である場合は、移植性の低いものを使用できます。
getline()
はPOSIX標準getline()
関数とは異なることに注意してください。
静的割り当てのための行を読み取るための非常に単純ですが安全ではない実装:
char line[1024];
scanf("%[^\n]", line);
バッファオーバーフローの可能性はないが、行全体を読み取れない可能性がある、より安全な実装は次のとおりです。
char line[1024];
scanf("%1023[^\n]", line);
変数の宣言で指定された長さとフォーマット文字列で指定された長さの「1による差」ではありません。それは歴史的な遺物です。
gets
が標準から完全に削除された理由
したがって、コマンド引数を探している場合は、Timの答えを見てください。コンソールから行を読みたいだけなら:
#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を選択した場合よりもはるかに多くの間違いを犯すことになります;)
gets
これはもう存在しないため、C11では機能しません。
getline
実行可能な例
これはPOSIX 7であり、メモリを割り当て、割り当てられたバッファをループでうまく再利用します。
ポインタnewbs、これを読んでください:getlineの最初の引数が「char *」ではなく「char **」へのポインタなのはなぜですか?
#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倍にし、スレッドセーフに見えます。
マクロを拡張する必要がありますが、それ以上のことはできません。
len
ここでの目的は何ですか?読んでも長さが
man getline
。len
は既存のバッファの長さで、0
魔法であり、割り当てるように指示します。Readは、読み取った文字数です。バッファサイズはより大きい場合がありread
ます。
私と同じように、多くの人がこの投稿にアクセスしますが、タイトルは検索されるものと一致しますが、説明では可変長について述べています。ほとんどの場合、長さは事前にわかっています。
事前に長さがわかっている場合は、以下を試してください。
char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read
ソース:https : //www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
提案されているように、行の終わりまたはEOFが返されるまでコンソールからgetchar()を使用して読み取り、独自のバッファーを構築できます。適切な最大行サイズを設定できない場合、動的にバッファが増大する可能性があります。
Cのnullで終了する文字列として行を取得する安全な方法として、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'に設定すると便利です)。
fgetsはline []をオーバーフィルせず、正常に戻ったときに最後に受け入れられた文字の後にnullがあることを保証します。
行末に達した場合、終了 '\ 0'の前の文字は '\ n'になります。
終了 '\ 0'の前に終了 '\ n'がない場合は、さらにデータがあるか、次の要求がファイルの終わりを報告する可能性があります。どちらがどれかを判別するには、別のfgetsを実行する必要があります。(この点で、getchar()でループする方が簡単です。)
上記の(更新された)コード例では、fgetsが成功した後でline [sizeof(line)-1] == '\ 0'の場合、バッファーが完全に満たされていることがわかります。そのポジションが '\ n'によって進められている場合、あなたはラッキーだったことがわかります。それ以外の場合は、stdinの前にさらに多くのデータまたはファイルの終わりがあります。(バッファが完全に満たされていない場合でも、ファイルの終わりにいる可能性があり、現在の行の最後に「\ n」がない可能性があります。文字列をスキャンして検索および/またはまたは、文字列の終わりの前にある '\ n'(バッファ内の最初の '\ 0')を削除すると、最初にgetchar()を使用する傾向があります。)
最初のチャンクとして読み取った量よりも多くの行がある場合に対処するために必要なことを行います。バッファを動的に拡張する例は、getcharまたはfgetsのいずれかで機能するように作成できます。注意が必要なトリッキーなエッジケースがいくつかあります(次の入力が、バッファーが拡張される前に前の入力を終了した '\ 0'の位置から格納を開始することを覚えているなど)。
Cのコンソールから行を読み取る方法は?
独自の関数を作成することは、コンソールから行を読み取るのに役立つ方法の1つです。
動的メモリ割り当てを使用して、必要なメモリ量を割り当てています
割り当てられたメモリを使い果たしそうになると、メモリのサイズを2倍にしようとします
そして、ここではループを使用してgetchar()
、ユーザーが'\n'
またはEOF
文字を入力するまで、関数を使用して文字列の各文字を1つずつスキャンしています
最後に、行を返す前に、追加で割り当てられたメモリを削除します
//the function to read lines of variable length
char* scan_line(char *line)
{
int ch; // as getchar() returns `int`
long capacity = 0; // capacity of the buffer
long length = 0; // maintains the length of the string
char *temp = NULL; // use additional pointer to perform allocations in order to avoid memory leaks
while ( ((ch = getchar()) != '\n') && (ch != EOF) )
{
if((length + 1) >= capacity)
{
// resetting capacity
if (capacity == 0)
capacity = 2; // some initial fixed length
else
capacity *= 2; // double the size
// try reallocating the memory
if( (temp = realloc(line, capacity * sizeof(char))) == NULL ) //allocating memory
{
printf("ERROR: unsuccessful allocation");
// return line; or you can exit
exit(1);
}
line = temp;
}
line[length] = (char) ch; //type casting `int` to `char`
}
line[length + 1] = '\0'; //inserting null character at the end
// remove additionally allocated memory
if( (temp = realloc(line, (length + 1) * sizeof(char))) == NULL )
{
printf("ERROR: unsuccessful allocation");
// return line; or you can exit
exit(1);
}
line = temp;
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!
出力例:
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;
}
goto
エラーケースの処理にを使用すると、コードがはるかに簡単になります。それにもかかわらず、ループ内で同じサイズで何度も繰り返し使用するtmp_buf
代わりに、を再利用できると思いませんmalloc
か?
has_err
を使用してエラーを報告すると、この関数はスレッドに対して安全でなくなり、使いづらくなります。そのようにしないでください。NULLを返すことにより、すでにエラーを示しています。また、表示されたエラーメッセージは、汎用ライブラリ関数では良い考えではないと考える余地もあります。
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;
}
コンソールから行を読み取るための最良かつ最も簡単な方法は、getchar()関数を使用することです。これにより、一度に1文字を配列に格納します。
{
char message[N]; /* character array for the message, you can always change the character length */
int i = 0; /* loop counter */
printf( "Enter a message: " );
message[i] = getchar(); /* get the first character */
while( message[i] != '\n' ){
message[++i] = getchar(); /* gets the next character */
}
printf( "Entered message is:" );
for( i = 0; i < N; i++ )
printf( "%c", message[i] );
return ( 0 );
}
この関数はあなたが望むことをするはずです:
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 );
これがお役に立てば幸いです。
fgets( buffer, sizeof(buffer), file );
ないでくださいsizeof(buffer)-1
。fgets
終了ヌルのためのスペースを残します。
while (!feof(file))
は常に間違っていることに注意してください。これは誤った使用のもう1つの例にすぎません。