Apa cara termudah untuk membaca baris lengkap dalam program konsol C Teks yang dimasukkan mungkin memiliki panjang variabel dan kami tidak dapat membuat asumsi apa pun tentang isinya.
Apa cara termudah untuk membaca baris lengkap dalam program konsol C Teks yang dimasukkan mungkin memiliki panjang variabel dan kami tidak dapat membuat asumsi apa pun tentang isinya.
Jawaban:
Anda membutuhkan manajemen memori dinamis, dan menggunakan fgets
fungsi tersebut untuk membaca baris Anda. Namun, sepertinya tidak ada cara untuk melihat berapa banyak karakter yang dibaca. Jadi Anda menggunakan 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;
}
Catatan : Jangan pernah gunakan mendapat! Itu tidak melakukan pemeriksaan batas dan dapat meluap buffer Anda
fgetc_unlocked
jika keamanan benang bukan masalah, tetapi kinerjanya lebih baik.
getline()
berbeda dari getline()
fungsi standar POSIX .
Jika Anda menggunakan pustaka GNU C atau pustaka lain yang mendukung POSIX, Anda dapat menggunakan getline()
dan meneruskannya stdin
untuk aliran file.
Penerapan yang sangat sederhana namun tidak aman untuk membaca baris untuk alokasi statis:
char line[1024];
scanf("%[^\n]", line);
Implementasi yang lebih aman, tanpa kemungkinan buffer overflow, tetapi dengan kemungkinan tidak membaca keseluruhan baris, adalah:
char line[1024];
scanf("%1023[^\n]", line);
Bukan 'perbedaan satu' antara panjang yang ditentukan yang menyatakan variabel dan panjang yang ditentukan dalam format string. Itu adalah artefak sejarah.
gets
dihapus dari standar sama sekali
Jadi, jika Anda mencari argumen perintah, lihat jawaban Tim. Jika Anda hanya ingin membaca baris dari konsol:
#include <stdio.h>
int main()
{
char string [256];
printf ("Insert your full address: ");
gets (string);
printf ("Your address is: %s\n",string);
return 0;
}
Ya, ini tidak aman, Anda dapat melakukan buffer overrun, tidak memeriksa akhir file, tidak mendukung pengkodean dan banyak hal lainnya. Sebenarnya saya bahkan tidak berpikir apakah itu melakukan hal-hal ini. Saya setuju saya agak kacau :) Tapi ... ketika saya melihat pertanyaan seperti "Bagaimana membaca baris dari konsol di C?", Saya berasumsi seseorang membutuhkan sesuatu yang sederhana, seperti gets () dan bukan 100 baris kode seperti di atas. Sebenarnya, saya pikir, jika Anda mencoba menulis 100 baris kode itu dalam kenyataan, Anda akan melakukan lebih banyak kesalahan, daripada yang akan Anda lakukan jika Anda memilih mendapatkan;)
gets
tidak ada lagi, jadi ini tidak berfungsi di C11.
getline
contoh yang dapat dijalankan
Disebutkan pada jawaban ini tetapi berikut adalah contohnya.
Ini adalah POSIX 7 , mengalokasikan memori untuk kita, dan menggunakan kembali buffer yang dialokasikan pada sebuah loop dengan baik.
Pointer newbs, baca ini: Mengapa argumen pertama getline adalah pointer ke pointer "char **" bukan "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;
}
implementasi glibc
Tidak ada POSIX? Mungkin Anda ingin melihat implementasi glibc 2.23 .
Ini memutuskan getdelim
, yang merupakan superset POSIX sederhana getline
dengan terminator baris arbitrer.
Ini menggandakan memori yang dialokasikan setiap kali peningkatan diperlukan, dan terlihat aman untuk thread.
Ini membutuhkan beberapa ekspansi makro, tetapi Anda tidak mungkin melakukannya dengan lebih baik.
len
di sini, ketika membaca memberikan panjangnya juga
man getline
. len
adalah panjang buffer yang ada, 0
merupakan keajaiban dan memerintahkannya untuk mengalokasikan. Baca adalah jumlah karakter yang dibaca. Ukuran buffer bisa lebih besar dari read
.
Banyak orang, seperti saya, datang ke posting ini dengan judul yang sesuai dengan yang dicari, meskipun uraiannya mengatakan tentang variabel panjang. Untuk kebanyakan kasus, kami mengetahui panjangnya terlebih dahulu.
Jika Anda tahu panjang sebelumnya, coba di bawah ini:
char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read
sumber: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
Seperti yang disarankan, Anda bisa menggunakan getchar () untuk membaca dari konsol sampai akhir baris atau EOF dikembalikan, membangun buffer Anda sendiri. Buffer yang berkembang secara dinamis dapat terjadi jika Anda tidak dapat menetapkan ukuran baris maksimum yang wajar.
Anda juga dapat menggunakan fgets sebagai cara yang aman untuk mendapatkan baris sebagai string C yang diakhiri dengan null:
#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);
Jika Anda telah kehabisan input konsol atau jika operasi gagal karena beberapa alasan, eof == NULL dikembalikan dan buffer baris mungkin tidak berubah (itulah mengapa pengaturan karakter pertama ke '\ 0' berguna).
fgets tidak akan memenuhi baris [] dan ini akan memastikan bahwa ada null setelah karakter yang diterima terakhir pada pengembalian yang berhasil.
Jika akhir baris tercapai, karakter sebelum penghentian '\ 0' akan menjadi '\ n'.
Jika tidak ada penghentian '\ n' sebelum akhiran '\ 0' mungkin ada lebih banyak data atau permintaan berikutnya akan melaporkan akhir file. Anda harus membuat widget lain untuk menentukan yang mana. (Dalam hal ini, melakukan perulangan dengan getchar () lebih mudah.)
Dalam kode contoh (diperbarui) di atas, jika baris [sizeof (line) -1] == '\ 0' setelah berhasil membuat widget, Anda tahu bahwa buffer telah terisi penuh. Jika posisi itu dilanjutkan dengan '\ n' Anda tahu Anda beruntung. Jika tidak, ada lebih banyak data atau akhir file di depan di stdin. (Ketika buffer tidak terisi sepenuhnya, Anda mungkin masih berada di akhir file dan mungkin juga tidak ada '\ n' di akhir baris saat ini. Karena Anda harus memindai string untuk menemukan dan / atau menghilangkan '\ n' sebelum akhir string ('\ 0' pertama di buffer), saya cenderung lebih suka menggunakan getchar () di tempat pertama.)
Lakukan apa yang perlu Anda lakukan untuk menghadapi masih ada lebih banyak baris daripada jumlah yang Anda baca sebagai bagian pertama. Contoh buffer yang tumbuh secara dinamis dapat dibuat untuk bekerja dengan getchar atau fgets. Ada beberapa kasus tepi yang rumit yang harus diperhatikan (seperti mengingat agar masukan berikutnya mulai disimpan pada posisi '\ 0' yang mengakhiri masukan sebelumnya sebelum buffer diperpanjang).
Bagaimana cara membaca baris dari konsol di C?
Membangun fungsi Anda sendiri, adalah salah satu cara yang akan membantu Anda mencapai pembacaan baris dari konsol
Saya menggunakan alokasi memori dinamis untuk mengalokasikan jumlah memori yang dibutuhkan
Saat kami akan menghabiskan memori yang dialokasikan, kami mencoba menggandakan ukuran memori
Dan di sini saya menggunakan loop untuk memindai setiap karakter dari string satu per satu menggunakan getchar()
fungsi hingga pengguna memasukkan '\n'
atau EOF
karakter
akhirnya kami menghapus memori tambahan yang dialokasikan sebelum mengembalikan baris
//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;
}
Sekarang Anda bisa membaca baris lengkapnya seperti ini:
char *line = NULL;
line = scan_line(line);
Berikut contoh program yang menggunakan scan_line()
fungsi tersebut:
#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
}
masukan sampel:
Twinkle Twinkle little star.... in the sky!
keluaran sampel:
Twinkle Twinkle little star.... in the sky!
Saya menemukan masalah yang sama beberapa waktu lalu, ini adalah solusi saya, semoga membantu.
/*
* 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
untuk menangani kasus kesalahan. Namun demikian, tidakkah Anda pikir Anda bisa menggunakan kembali tmp_buf
, alih-alih malloc
menggunakannya dengan ukuran yang sama berulang kali dalam satu lingkaran?
has_err
untuk melaporkan kesalahan membuat fungsi ini tidak aman untuk thread dan kurang nyaman digunakan. Jangan lakukan seperti itu. Anda sudah menunjukkan kesalahan dengan mengembalikan NULL. Ada juga ruang untuk berpikir bahwa pesan kesalahan yang dicetak bukanlah ide yang baik dalam fungsi perpustakaan serba guna.
Di sistem BSD dan Android Anda juga dapat menggunakan fgetln
:
#include <stdio.h>
char *
fgetln(FILE *stream, size_t *len);
Seperti:
size_t line_len;
const char *line = fgetln(stdin, &line_len);
The line
tidak nol dihentikan dan berisi \n
(atau apa pun platform Anda menggunakan) pada akhirnya. Ini menjadi tidak valid setelah operasi I / O berikutnya on stream.
Sesuatu seperti ini:
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;
}
Cara terbaik dan termudah untuk membaca baris dari konsol adalah menggunakan fungsi getchar (), di mana Anda akan menyimpan satu karakter pada satu waktu dalam sebuah array.
{
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 );
}
Fungsi ini harus melakukan apa yang Anda inginkan:
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 );
Saya harap ini membantu.
fgets( buffer, sizeof(buffer), file );
tidak sizeof(buffer)-1
. fgets
meninggalkan ruang untuk mengakhiri null.
while (!feof(file))
itu selalu salah dan ini hanyalah satu lagi contoh penggunaan yang salah.