web-dev-qa-db-ja.com

Cのコンソールから行を読み取る方法

Cコンソールプログラムで完全な行を読む最も簡単な方法は何ですか入力されたテキストは可変長である可能性があり、その内容について推測することはできません。

97
pbreault

動的メモリ管理が必要であり、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;
}

:決してgetsを使用しないでください!境界チェックを行わず、バッファをオーバーフローさせる可能性があります

GNU Cライブラリまたは別のPOSIX準拠ライブラリを使用している場合、 getline() を使用して、ファイルストリーム用にstdinを渡すことができます。

27
dmityugov

静的割り当ての行を読み取るための非常に単純だが安全でない実装:

char line[1024];

scanf("%[^\n]", line);

バッファオーバーフローの可能性はないが、行全体を読み取らない可能性がある、より安全な実装は次のとおりです。

char line[1024];

scanf("%1023[^\n]", line);

変数を宣言する指定された長さとフォーマット文字列で指定された長さの間の「差」ではありません。それは歴史的な工芸品です。

15
Anish Jhaveri

バッファオーバーフローが発生せず、入力を切り捨てないようにするために、文字ごと(getc())ループを使用する必要がある場合があります。

11
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を選択した場合よりも多くの間違いを犯すことになります;)

9
badbadboy

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倍にし、スレッドセーフに見えます。

マクロを拡張する必要がありますが、これ以上改善する可能性は低いでしょう。

提案されているように、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」に設定すると便利です)。

fgetsはline []を過剰に埋めることはなく、正常に戻ったときに最後に受け入れられた文字の後にnullが存在することを保証します。

行末に達した場合、終了する「\ 0」の前の文字は「\ n」になります。

終了 '\ 0'の前に終了 '\ n'がない場合は、データがさらにあるか、次の要求がファイルの終わりを報告する可能性があります。どちらがどちらであるかを判断するには、別のfgetsを実行する必要があります。 (この点で、getchar()を使用したループは簡単です。)

上記の(更新された)サンプルコードでは、fgetが成功した後にline [sizeof(line)-1] == '\ 0'の場合、バッファが完全に満たされていることがわかります。そのポジションが「\ n」によって進められている場合、あなたは幸運だったとわかります。それ以外の場合は、stdinにさらにデータがあるか、ファイルの終わりが先にあります。 (バッファが完全にいっぱいにならない場合でも、ファイルの終わりにいる可能性があり、現在の行の終わりに「\ n」がないこともあります。文字列をスキャンして、または、文字列の最後(バッファ内の最初の '\ 0')の前にある '\ n'を削除すると、最初の場所でgetchar()を使用することを好みます。)

最初のチャンクとして読む量よりも多くの行が残っている場合に対処するために必要なことを行います。バッファを動的に拡張する例は、getcharまたはfgetsで動作するように作成できます。注意すべきいくつかのトリッキーなEdgeのケースがあります(バッファが拡張される前に、前の入力を終了した「\ 0」の位置で次の入力の保存を開始することを思い出すなど)。

5
orcmid

私のような多くの人々が、この説明にタイトルが検索対象に一致するようになっていますが、説明では可変長について述べています。ほとんどの場合、長さは事前にわかっています。

長さを事前に知っている場合は、以下を試してください:

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のコンソールから行を読み取る方法

  • 独自の関数を構築するは、でコンソールから行を読み取るのに役立つ方法の1つですC

  • 私は dynamic memory allocation を使用して十分な量を割り当てています'\0'文字とともに行のすべての文字を保持するために必要なメモリ。

  • そしてここでは、ユーザーが'\n'またはEOF文字を入力するまで、getchar()関数を使用して文字列の各文字を1つずつスキャンするループを使用しています。

    //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()関数を使用したexample programは次のとおりです。

#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!
1
Cherubim

このようなもの:

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;
}
0
user2074102

私はしばらく前に同じ問題に出くわしました。これが私の解決策であり、助けになることを願っています。

/*
 * 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;
}
0
dsm

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操作の後に無効になります。

0
wonder.mice