Odpowiedzi:
Potrzebujesz dynamicznego zarządzania pamięcią i użyj tej fgets
funkcji do odczytania linii. Jednak wydaje się, że nie ma sposobu, aby sprawdzić, ile znaków przeczytał. Więc używasz 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;
}
Uwaga : nigdy nie używaj pobiera! Nie sprawdza granic i może przepełnić twój bufor
fgetc_unlocked
jeśli bezpieczeństwo wątków nie jest problemem, ale wydajność.
getline()
różni się to od standardowej getline()
funkcji POSIX .
Bardzo prosta, ale niebezpieczna implementacja do odczytu wiersza dla statycznej alokacji:
char line[1024];
scanf("%[^\n]", line);
Bezpieczniejszą implementacją, bez możliwości przepełnienia bufora, ale z możliwością nie odczytania całej linii, jest:
char line[1024];
scanf("%1023[^\n]", line);
Nie „różnica o jeden” między długością określoną w deklaracji zmiennej a długością określoną w ciągu formatu. To historyczny artefakt.
gets
został całkowicie usunięty ze standardu
Tak więc, jeśli szukałeś argumentów poleceń, spójrz na odpowiedź Tima. Jeśli chcesz tylko przeczytać wiersz z konsoli:
#include <stdio.h>
int main()
{
char string [256];
printf ("Insert your full address: ");
gets (string);
printf ("Your address is: %s\n",string);
return 0;
}
Tak, to nie jest bezpieczne, można zrobić przepełnienie bufora, nie sprawdza końca pliku, nie obsługuje kodowania i wielu innych rzeczy. Właściwie nawet nie pomyślałem, czy to zrobiło JAKIEKOLWIEK z tych rzeczy. Zgadzam się, trochę schrzaniłem :) Ale ... kiedy widzę pytanie typu "Jak odczytać wiersz z konsoli w C?", Zakładam, że dana osoba potrzebuje czegoś prostego, na przykład gets (), a nie 100 linii kodu jak wyżej. Właściwie myślę, że gdybyś spróbował napisać te 100 linijek kodu w rzeczywistości, zrobiłbyś o wiele więcej błędów, niż gdybyś wybrał opcję dostaje;)
gets
już nie istnieje, więc to nie działa w C11.
getline
przykład do uruchomienia
Wspomniano o tej odpowiedzi, ale oto przykład.
Jest zgodny z POSIX 7 , alokuje nam pamięć i ładnie wykorzystuje przydzielony bufor w pętli.
Pointer newbs, przeczytaj to: Dlaczego pierwszy argument getline jest wskaźnikiem do wskaźnika „char **” zamiast „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;
}
implementacja glibc
Brak POSIX? Może chcesz przyjrzeć się implementacji glibc 2.23 .
Rozwiązuje się to getdelim
, co jest prostym zestawem POSIX getline
z dowolnym terminatorem linii.
Podwaja przydzieloną pamięć, gdy jest potrzebne zwiększenie, i wygląda na bezpieczną wątkowo.
Wymaga rozszerzenia makr, ale jest mało prawdopodobne, abyś zrobił coś lepszego.
len
tutaj, kiedy czytamy, podajemy również długość
man getline
. len
jest długością istniejącego bufora, 0
jest magiczne i mówi mu, aby go przydzielił. Odczyt to liczba odczytanych znaków. Rozmiar bufora może być większy niż read
.
Wiele osób, tak jak ja, przychodzi do tego posta z tytułem pasującym do tego, czego szukamy, chociaż opis mówi o zmiennej długości. W większości przypadków długość znamy wcześniej.
Jeśli znasz długość przed ręką, spróbuj poniżej:
char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read
źródło: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
Jak sugerowano, możesz użyć getchar () do czytania z konsoli aż do zwrócenia końca linii lub EOF, budując swój własny bufor. Dynamiczne powiększanie bufora może nastąpić, jeśli nie możesz ustawić rozsądnego maksymalnego rozmiaru linii.
Możesz również użyć fgets jako bezpiecznego sposobu na uzyskanie wiersza jako łańcucha zakończonego znakiem null w języku C:
#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);
Jeśli wyczerpałeś dane wejściowe konsoli lub operacja się nie powiodła z jakiegoś powodu, zwracany jest eof == NULL i bufor linii może pozostać niezmieniony (dlatego ustawienie pierwszego znaku na „\ 0” jest przydatne).
fgets nie przepełni linii [] i zapewni, że po ostatnim zaakceptowanym znaku po pomyślnym powrocie będzie istniał znak null.
Jeśli osiągnięto koniec linii, znak poprzedzający kończący „\ 0” będzie „\ n”.
Jeśli nie ma końca „\ n” przed zakończeniem „\ 0”, może to oznaczać, że jest więcej danych lub że następne żądanie zgłosi koniec pliku. Będziesz musiał zrobić kolejne fgets, aby określić, który jest który. (Pod tym względem pętle z getchar () są łatwiejsze.)
W (zaktualizowanym) przykładowym kodzie powyżej, jeśli linia [sizeof (linia) -1] == '\ 0' po udanych fgets, wiesz, że bufor został całkowicie zapełniony. Jeśli ta pozycja jest poprzedzona znakiem „\ n”, wiesz, że miałeś szczęście. W przeciwnym razie w stdin jest albo więcej danych, albo koniec pliku z przodu. (Gdy bufor nie jest całkowicie wypełniony, nadal możesz znajdować się na końcu pliku, a na końcu bieżącej linii może również nie być znaku „\ n”. Ponieważ musisz przeskanować ciąg, aby znaleźć i / lub wyeliminować jakiekolwiek „\ n” przed końcem ciągu (pierwsze „\ 0” w buforze), jestem skłonny w pierwszej kolejności używać getchar ().)
Zrób to, co musisz zrobić, aby poradzić sobie z tym, że nadal jest więcej linii niż ta, którą czytasz jako pierwszy fragment. Przykłady dynamicznie rosnącego bufora można wykorzystać do pracy z getchar lub fgets. Istnieje kilka trudnych przypadków brzegowych, na które należy uważać (np. Pamiętanie, aby następne wejście zaczęło zapisywać w pozycji '\ 0', która kończyła poprzednie wejście przed rozszerzeniem bufora).
Jak odczytać wiersz z konsoli w C?
Budowanie własnej funkcji jest jednym ze sposobów, które pomogłyby w czytaniu wiersza z konsoli
Używam dynamicznej alokacji pamięci, aby przydzielić wymaganą ilość pamięci
Kiedy zbliżamy się do wyczerpania przydzielonej pamięci, staramy się podwoić jej rozmiar
I tutaj używam pętli do skanowania każdego znaku ciągu jeden po drugim, używając getchar()
funkcji, aż użytkownik wprowadzi znak '\n'
lubEOF
na koniec usuwamy wszelką dodatkowo przydzieloną pamięć przed zwróceniem wiersza
//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;
}
Teraz możesz przeczytać całą linię w ten sposób:
char *line = NULL;
line = scan_line(line);
Oto przykładowy program korzystający z scan_line()
funkcji:
#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
}
przykładowe dane wejściowe:
Twinkle Twinkle little star.... in the sky!
przykładowe wyjście:
Twinkle Twinkle little star.... in the sky!
Z tym samym problemem natknąłem się jakiś czas temu, to było moje rozwiązanie, mam nadzieję, że pomoże.
/*
* 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
do obsługi przypadku błędu. Niemniej jednak, czy nie sądzisz, że mógłbyś użyć go ponownie tmp_buf
, zamiast używać malloc
go w kółko w tym samym rozmiarze?
has_err
do zgłaszania błędów sprawia, że ta funkcja jest niebezpieczna dla wątków i mniej niż wygodna w użyciu. Nie rób tego w ten sposób. Już wskazałeś błąd, zwracając NULL. Można również pomyśleć, że drukowane komunikaty o błędach nie są dobrym pomysłem w przypadku funkcji biblioteki ogólnego przeznaczenia.
W systemach BSD i Android możesz również użyć fgetln
:
#include <stdio.h>
char *
fgetln(FILE *stream, size_t *len);
Tak jak to:
size_t line_len;
const char *line = fgetln(stdin, &line_len);
To line
nie jest zerowe i zawiera \n
(lub cokolwiek używa twoja platforma) na końcu. Staje się nieważny po następnej operacji we / wy w strumieniu.
Coś takiego:
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;
}
Najlepszym i najprostszym sposobem odczytania wiersza z konsoli jest użycie funkcji getchar (), dzięki której będziesz przechowywać po jednym znaku w tablicy.
{
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 );
}
Ta funkcja powinna robić to, co chcesz:
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 );
Mam nadzieję, że to pomoże.
fgets( buffer, sizeof(buffer), file );
nie sizeof(buffer)-1
. fgets
pozostawia miejsce na kończącą wartość null.
while (!feof(file))
jest to zawsze błędne i jest to jeszcze jeden przykład błędnego użycia.