Was ist der einfachste Weg, eine vollständige Zeile in einem C-Konsolenprogramm zu lesen? Der eingegebene Text hat möglicherweise eine variable Länge und wir können keine Annahme über den Inhalt treffen.
Was ist der einfachste Weg, eine vollständige Zeile in einem C-Konsolenprogramm zu lesen? Der eingegebene Text hat möglicherweise eine variable Länge und wir können keine Annahme über den Inhalt treffen.
Antworten:
Sie benötigen eine dynamische Speicherverwaltung und verwenden die fgets
Funktion zum Lesen Ihrer Zeile. Es scheint jedoch keine Möglichkeit zu geben, zu sehen, wie viele Zeichen es liest. 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 überprüft keine Grenzen und kann Ihren Puffer überlaufen lassen
fgetc_unlocked
wenn die Thread-Sicherheit kein Problem darstellt, die Leistung jedoch.
getline()
von der POSIX-Standardfunktion unterscheidet getline()
.
Eine sehr einfache, aber unsichere Implementierung zum Lesen der Zeile für die statische Zuordnung:
char line[1024];
scanf("%[^\n]", line);
Eine sicherere Implementierung ohne die Möglichkeit eines Pufferüberlaufs, aber mit der Möglichkeit, nicht die gesamte Zeile zu lesen, ist:
char line[1024];
scanf("%1023[^\n]", line);
Nicht die 'Differenz um eins' zwischen der angegebenen Länge, die die Variable deklariert, und der in der Formatzeichenfolge angegebenen Länge. Es ist ein historisches Artefakt.
gets
insgesamt aus dem Standard entfernt wurde
Wenn Sie also nach Befehlsargumenten suchen, werfen Sie einen Blick auf Tims Antwort. Wenn Sie nur eine Zeile von der Konsole 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 einen Pufferüberlauf durchführen, es wird nicht nach Dateiende gesucht, es werden keine Codierungen und viele andere Dinge unterstützt. Eigentlich habe ich nicht einmal darüber nachgedacht, ob es irgendetwas davon getan hat. Ich bin damit einverstanden, dass ich es irgendwie vermasselt habe :) 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 get () und nicht 100 Codezeilen wie oben. Eigentlich denke ich, wenn Sie versuchen, diese 100 Codezeilen in der Realität zu schreiben, würden Sie viel mehr Fehler machen, als Sie getan hätten, wenn Sie sich dafür entschieden hätten;)
gets
existiert nicht mehr, daher funktioniert dies in C11 nicht.
getline
lauffähiges Beispiel
Erwähnt zu dieser Antwort, aber hier ist ein Beispiel.
Es ist POSIX 7, weist uns Speicher zu und verwendet den zugewiesenen Puffer in einer Schleife gut wieder.
Zeiger newbs, lesen Sie Folgendes: Warum ist das erste Argument von getline ein Zeiger auf den 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 ansehen .
Es wird aufgelöst in getdelim
, was eine einfache POSIX-Obermenge von getline
mit einem beliebigen Zeilenabschluss ist.
Es verdoppelt den zugewiesenen Speicher, wenn eine Erhöhung erforderlich ist, und sieht threadsicher aus.
Es erfordert eine gewisse Makroerweiterung, aber es ist unwahrscheinlich, dass Sie es besser machen.
len
hier, wenn das Lesen auch die Länge liefert
man getline
. len
ist die Länge des vorhandenen Puffers, 0
ist magisch und weist ihn an, zuzuweisen. Lesen ist die Anzahl der gelesenen Zeichen. Die Puffergröße könnte größer sein als read
.
Viele Leute, wie ich, kommen zu diesem Beitrag mit dem Titel, der dem entspricht, wonach gesucht wird, obwohl die Beschreibung über variable Länge sagt. In den meisten Fällen kennen wir die Länge im Voraus.
Wenn Sie die Länge vorher kennen, versuchen Sie es unten:
char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read
Quelle: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
Wie vorgeschlagen, können Sie mit getchar () von der Konsole lesen, bis ein Zeilenende oder ein EOF zurückgegeben wird, und Ihren eigenen Puffer erstellen. Das dynamische Wachsen des Puffers kann auftreten, wenn Sie keine angemessene maximale Zeilengröße festlegen können.
Sie können auch fgets verwenden, um eine Zeile als C-nullterminierte Zeichenfolge auf sichere Weise abzurufen:
#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 der Vorgang aus irgendeinem Grund fehlgeschlagen ist, wird eof == NULL zurückgegeben und der Zeilenpuffer bleibt möglicherweise unverändert (weshalb es praktisch ist, das erste Zeichen auf '\ 0' zu setzen).
fgets überfüllt die Zeile [] nicht und stellt sicher, dass bei einer erfolgreichen Rückgabe nach dem zuletzt akzeptierten Zeichen eine Null steht.
Wenn das Zeilenende erreicht wurde, ist das Zeichen vor dem abschließenden '\ 0' ein '\ n'.
Wenn vor dem Ende von '\ 0' kein '\ n' beendet wird, kann es sein, dass mehr Daten vorhanden sind oder dass die nächste Anforderung das Dateiende meldet. Sie müssen weitere Fgets ausführen, um festzustellen, welches welches ist. (In dieser Hinsicht ist das Schleifen mit getchar () einfacher.)
Im obigen (aktualisierten) Beispielcode wissen Sie, dass der Puffer vollständig gefüllt wurde, wenn Zeile [sizeof (Zeile) -1] == '\ 0' nach erfolgreichen Fgets ist. Wenn diese Position von einem '\ n' besetzt wird, wissen Sie, dass Sie Glück hatten. Andernfalls liegen in stdin entweder mehr Daten oder ein Dateiende vor uns. (Wenn der Puffer nicht vollständig gefüllt ist, befindet sich möglicherweise immer noch ein Dateiende und am Ende der aktuellen Zeile befindet sich möglicherweise kein '\ n'. Da Sie die Zeichenfolge scannen müssen, um und / oder zu finden oder ein '\ n' vor dem Ende des Strings entfernen (das erste '\ 0' im Puffer), ich bin geneigt, getchar () überhaupt zu verwenden.)
Tun Sie, was Sie tun müssen, um damit umzugehen, dass immer noch mehr Zeilen als der Betrag vorhanden sind, den Sie als ersten Block gelesen haben. Die Beispiele für das dynamische Wachsen eines Puffers können entweder mit getchar oder fgets verwendet werden. Es gibt einige knifflige Randfälle, auf die Sie achten müssen (z. B. das Erinnern daran, dass der nächste Eingang an der Position '\ 0' gespeichert wird, die den vorherigen Eingang beendet hat, bevor der Puffer erweitert wurde).
Wie lese ich eine Zeile von der Konsole in C?
Das Erstellen einer eigenen Funktion ist eine der Möglichkeiten, mit denen Sie eine Zeile von der Konsole aus lesen können
Ich verwende die dynamische Speicherzuweisung , um die erforderliche Speichermenge zuzuweisen
Wenn wir den zugewiesenen Speicher erschöpfen wollen, versuchen wir, die Größe des Speichers zu verdoppeln
Und hier bin ich mit einer Schleife jedes Zeichen der Zeichenfolge ein scannen , indem man mit der getchar()
Funktion , bis der Benutzer eingibt '\n'
oder EOF
Zeichen
Schließlich entfernen wir den zusätzlich zugewiesenen Speicher, bevor wir die Zeile zurückgeben
//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;
}
Jetzt können Sie eine vollständige Zeile folgendermaßen lesen:
char *line = NULL;
line = scan_line(line);
Hier ist ein Beispielprogramm mit der scan_line()
Funktion:
#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!
Ich bin vor einiger Zeit auf dasselbe Problem gestoßen, dies 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;
}
goto
den Fehlerfall behandeln. Glauben Sie trotzdem nicht, dass Sie es wiederverwenden könnten tmp_buf
, anstatt malloc
es immer wieder mit der gleichen Größe in der Schleife zu verwenden?
has_err
zum Melden von Fehlern macht diese Funktion threadsicher und weniger benutzerfreundlich. Mach es nicht so. Sie geben bereits einen Fehler an, indem Sie NULL zurückgeben. Es gibt auch Raum zu der Annahme, dass die gedruckten Fehlermeldungen in einer universellen Bibliotheksfunktion keine gute Idee sind.
Auf BSD-Systemen und Android können Sie außerdem Folgendes verwenden fgetln
:
#include <stdio.h>
char *
fgetln(FILE *stream, size_t *len);
Wie so:
size_t line_len;
const char *line = fgetln(stdin, &line_len);
Das line
ist nicht nullterminiert und enthält \n
(oder was auch immer Ihre Plattform verwendet) am Ende. Sie wird nach der nächsten E / A-Operation im Stream ungültig.
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;
}
Der beste und einfachste Weg, eine Zeile von einer Konsole zu lesen, ist die Funktion getchar (), mit der Sie jeweils ein Zeichen in einem Array speichern.
{
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 );
}}
Diese Funktion sollte tun, was Sie wollen:
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 );
Ich hoffe das hilft.
fgets( buffer, sizeof(buffer), file );
nicht sizeof(buffer)-1
. fgets
lässt Platz für die abschließende Null.
while (!feof(file))
immer falsch ist und dies nur ein weiteres Beispiel für eine fehlerhafte Verwendung ist.