web-dev-qa-db-de.com

Wie liest man eine Zeile von der Konsole aus in C?

Was ist der einfachste Weg, um eine vollständige Zeile in einem C-Konsolenprogramm zu lesen. Der eingegebene Text kann eine variable Länge haben und wir können keine Annahme über den Inhalt machen.

96
pbreault

Sie benötigen eine dynamische Speicherverwaltung und lesen die Zeile mit der Funktion fgets. Es scheint jedoch keine Möglichkeit zu geben, wie viele Zeichen gelesen werden. Sie verwenden also 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;
}

Hinweis : Niemals verwenden bekommt! Es werden keine Grenzen geprüft und der Puffer kann überlaufen

Wenn Sie die C-Bibliothek GNU oder eine andere POSIX-kompatible Bibliothek verwenden, können Sie getline() verwenden und stdin für den Dateistream übergeben.

26
dmityugov

Eine sehr einfache, aber unsichere Implementierung, um die Zeile für die statische Zuordnung zu lesen: 

char line[1024];

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

Eine sicherere Implementierung ohne die Möglichkeit eines Pufferüberlaufs, jedoch mit der Möglichkeit, die gesamte Zeile nicht zu lesen, ist:

char line[1024];

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

Nicht die Differenz um 1 zwischen der angegebenen Länge, die die Variable deklariert, und der in der Formatzeichenfolge angegebenen Länge. Es ist ein historisches Artefakt.

14
Anish Jhaveri

Möglicherweise müssen Sie eine zeichenweise (getc ()) -Schleife verwenden, um sicherzustellen, dass kein Pufferüberlauf vorliegt und die Eingabe nicht abgeschnitten wird. 

11
Tim

Wenn Sie nach Befehlsargumenten suchen, werfen Sie einen Blick auf Tims Antwort. Wenn Sie nur eine Zeile von der Konsole aus lesen möchten:

#include <stdio.h>

int main()
{
  char string [256];
  printf ("Insert your full address: ");
  gets (string);
  printf ("Your address is: %s\n",string);
  return 0;
}

Ja, es ist nicht sicher, Sie können Pufferüberlauf ausführen, es wird nicht auf das Dateiende geprüft, es werden keine Kodierungen unterstützt und viele andere Sachen . Eigentlich habe ich nicht einmal gedacht, ob es überhaupt JEDEN tat von diesem Zeug. Ich stimme zu, dass ich irgendwie vermasselt bin:) Aber ... wenn ich eine Frage wie "Wie lese ich eine Zeile von der Konsole in C?" Sehe, gehe ich davon aus, dass eine Person etwas einfaches braucht, wie gets (). und nicht 100 Zeilen Code wie oben . Eigentlich denke ich, wenn Sie versuchen, diese 100 Zeilen Code in der Realität zu schreiben, würden Sie viel mehr Fehler machen, als Sie es getan hätten, wenn Sie sich entschieden hätten;)

8
badbadboy

getline lauffähiges Beispiel

Erwähnt auf diese Antwort aber hier ist ein Beispiel.

Es ist POSIX 7 , reserviert Speicher für uns und verwendet den zugewiesenen Puffer in einer Schleife gut.

Zeiger newbs, lesen Sie dies: Warum ist das erste Argument von getline ein Zeiger auf Zeiger "char **" anstelle von "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-implementierung

Kein POSIX? Vielleicht möchten Sie sich die Implementierung von glibc 2.23 anschauen.

Es löst sich in getdelim auf. Hierbei handelt es sich um eine einfache POSIX-Obermenge von getline mit einem beliebigen Zeilenabschlusszeichen.

Er verdoppelt den zugewiesenen Speicher, wenn eine Erhöhung erforderlich ist, und sieht Thread-sicher aus.

Es erfordert einige Makroerweiterungen, aber Sie werden wahrscheinlich nicht viel besser machen.

Wie vorgeschlagen, können Sie mit getchar () von der Konsole aus lesen, bis ein Zeilenende oder EOF zurückgegeben wird, um einen eigenen Puffer zu erstellen. Das dynamische Anwachsen des Puffers kann auftreten, wenn Sie keine vernünftige maximale Zeilengröße festlegen können.

Sie können auch Fgets als sichere Methode verwenden, um eine Zeile als nullterminierte C-Zeichenfolge zu erhalten:

#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);

Wenn Sie die Konsoleneingabe erschöpft haben oder die Operation aus irgendeinem Grund fehlgeschlagen ist, wird eof == NULL zurückgegeben, und der Zeilenpuffer ist möglicherweise unverändert (weshalb es praktisch ist, das erste Zeichen auf "\ 0" zu setzen).

fgets überfüllen Zeile [] nicht und es wird sichergestellt, dass nach dem letzten akzeptierten Zeichen bei einer erfolgreichen Rückkehr eine Null vorhanden ist.

Wenn das Zeilenende erreicht wurde, ist das Zeichen vor dem abschließenden '\ 0' ein '\ n'.

Wenn vor der Endung '\ 0' kein Abschluss '\ n' steht, kann es sein, dass weitere Daten vorhanden sind oder dass die nächste Anforderung das Dateiende meldet. Sie müssen noch ein paar Schritte machen, um herauszufinden, welche welche sind. (In dieser Hinsicht ist das Looping mit getchar () einfacher.)

Wenn im obigen (aktualisierten) Beispielcode Zeile [sizeof (line) -1] == '\ 0' nach erfolgreichen Zielvorgängen verwendet wird, wissen Sie, dass der Puffer vollständig gefüllt wurde. Wenn diese Position mit einem\n\n\n\n\n\n fortgesetzt wird, wissen Sie, dass Sie Glück hatten. Andernfalls stehen in stdin entweder mehr Daten oder ein Dateiende vor. (Wenn der Puffer nicht vollständig gefüllt ist, befinden Sie sich möglicherweise noch am Ende der Datei und am Ende der aktuellen Zeile befindet sich möglicherweise kein '\ n'.) Da müssen Sie die Zeichenfolge nach suchen und/oder ein beliebiges '\ n' vor dem Ende des Strings entfernen (das erste '\ 0' im Puffer), möchte ich lieber getchar () bevorzugen.)

Tun Sie, was Sie tun müssen, um damit fertig zu werden, da immer noch mehr Zeilen vorhanden sind als der Betrag, den Sie als ersten Block gelesen haben. Die Beispiele für das dynamische Wachsen eines Puffers können entweder mit getchar oder mit fgets arbeiten. Es gibt einige knifflige Edge-Fälle, auf die Sie achten müssen (z. B. daran zu erinnern, dass die nächste Eingabe an der Position des '\ 0' gespeichert wird, die die vorherige Eingabe beendet hat, bevor der Puffer erweitert wurde).

4
orcmid

Viele Leute wie ich kommen zu diesem Beitrag mit dem Titel, der dem entspricht, nach dem gesucht wird, obwohl die Beschreibung über variable Länge spricht. In den meisten Fällen kennen wir die Länge vorher. 

Wenn Sie die Länge vor der Hand kennen, versuchen Sie es unten:

char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read

source: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm

Wie liest man eine Zeile von der Konsole aus in C?

  • Das Erstellen einer eigenen Funktion ist eine der Möglichkeiten, mit der Sie das Lesen einer Zeile von der Konsole in C aus erreichen können.

  • Ich verwende dynamische Speicherzuordnung , um nur die ausreichende Menge an Speicher zuzuweisen, die erforderlich ist, um alle Zeichen einer Zeile zusammen mit dem Zeichen '\0' zu speichern.

  • Und hier verwende ich eine Schleife, um jedes Zeichen der Zeichenfolge mit der Funktion getchar() nacheinander zu scannen, bis der Benutzer das Zeichen '\n' oder EOF eingibt

    //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;
    }  
    
  • Nun könnten Sie eine vollständige Zeile so lesen:

    char *line = NULL;
    line = scan_line(line);
    

Hier ist ein Beispielprogramm mit der Funktion 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
}

Beispieleingabe:

Twinkle Twinkle little star.... in the sky!

Beispielausgabe:

Twinkle Twinkle little star.... in the sky!
1
Cherubim

Etwas wie das:

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

Ich bin vor einiger Zeit auf das gleiche Problem gestoßen, das war meine Lösung, ich hoffe es hilft.

/*
 * 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

Auf BSD-Systemen und Android können Sie auch fgetln verwenden:

#include <stdio.h>

char *
fgetln(FILE *stream, size_t *len);

So wie:

size_t line_len;
const char *line = fgetln(stdin, &line_len);

Die Variable line ist nicht null abgeschlossen und enthält am Ende \n (oder was auch immer Ihre Plattform verwendet). Sie wird nach der nächsten E/A-Operation im Stream ungültig.

0
wonder.mice