วิธีที่ง่ายที่สุดในการอ่านบรรทัดเต็มในโปรแกรมคอนโซล C คืออะไรข้อความที่ป้อนอาจมีความยาวผันแปรและเราไม่สามารถตั้งสมมติฐานเกี่ยวกับเนื้อหาได้
วิธีที่ง่ายที่สุดในการอ่านบรรทัดเต็มในโปรแกรมคอนโซล C คืออะไรข้อความที่ป้อนอาจมีความยาวผันแปรและเราไม่สามารถตั้งสมมติฐานเกี่ยวกับเนื้อหาได้
คำตอบ:
คุณต้องมีการจัดการหน่วยความจำแบบไดนามิกและใช้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;
}
หมายเหตุ : Never use gets! ไม่ทำการตรวจสอบขอบเขตและสามารถล้นบัฟเฟอร์ของคุณได้
fgetc_unlocked
หากความปลอดภัยของเธรดไม่ได้เป็นปัญหา แต่ประสิทธิภาพนั้น
getline()
แตกต่างจากgetline()
ฟังก์ชันมาตรฐาน POSIX
หากคุณกำลังใช้ไลบรารี GNU C หรือไลบรารีที่สอดคล้องกับ POSIX อื่นคุณสามารถใช้getline()
และส่งผ่านstdin
ไปยังไลบรารีสำหรับสตรีมไฟล์ได้
การใช้งานที่ง่ายมาก แต่ไม่ปลอดภัยในการอ่านบรรทัดสำหรับการจัดสรรแบบคงที่:
char line[1024];
scanf("%[^\n]", line);
การใช้งานที่ปลอดภัยยิ่งขึ้นโดยไม่มีความเป็นไปได้ที่จะเกิดบัฟเฟอร์ล้น แต่มีความเป็นไปได้ที่จะไม่อ่านทั้งบรรทัดคือ:
char line[1024];
scanf("%1023[^\n]", line);
ไม่ใช่ 'ความแตกต่างโดยหนึ่ง' ระหว่างความยาวที่ระบุประกาศตัวแปรและความยาวที่ระบุในสตริงรูปแบบ เป็นสิ่งประดิษฐ์ทางประวัติศาสตร์
gets
ถูกลบออกจากมาตรฐานทั้งหมด
ดังนั้นหากคุณกำลังมองหาข้อโต้แย้งคำสั่งลองดูคำตอบของ 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 ได้อย่างไร" ฉันคิดว่าคน ๆ หนึ่งต้องการอะไรง่ายๆเช่น gets () ไม่ใช่ 100 บรรทัดของรหัส เหมือนข้างบน. อันที่จริงฉันคิดว่าถ้าคุณพยายามเขียนโค้ด 100 บรรทัดในความเป็นจริงคุณจะทำผิดพลาดมากกว่าที่คุณจะทำถ้าคุณได้รับ;)
gets
ไม่มีอีกแล้วจึงใช้ไม่ได้ใน C11
คุณอาจต้องใช้อักขระทีละอักขระ (getc ()) ลูปเพื่อให้แน่ใจว่าคุณไม่มีบัฟเฟอร์ล้นและไม่ตัดทอนอินพุต
getline
ตัวอย่างที่รันได้
กล่าวถึงคำตอบนี้แต่นี่คือตัวอย่าง
มันคือPOSIX 7จัดสรรหน่วยความจำให้เราและนำบัฟเฟอร์ที่จัดสรรมาใช้ซ้ำบนลูปอย่างดี
Pointer 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
ซึ่งเป็นส่วนเหนือ POSIX แบบง่ายของgetline
ตัวยุติบรรทัดโดยพลการ
เพิ่มหน่วยความจำที่จัดสรรเป็นสองเท่าเมื่อจำเป็นต้องเพิ่มและดูปลอดภัย
ต้องมีการขยายมาโคร แต่คุณไม่น่าจะทำได้ดีกว่านี้
len
ที่นี่คืออะไรเมื่ออ่านแล้วให้ความยาวด้วย
man getline
. len
คือความยาวของบัฟเฟอร์ที่มีอยู่0
เป็นเวทมนตร์และบอกให้จัดสรร Read คือจำนวนตัวอักษรที่อ่าน read
ขนาดบัฟเฟอร์อาจจะมีขนาดใหญ่กว่า
หลายคนเช่นฉันมาที่โพสต์นี้พร้อมกับชื่อที่ตรงกับสิ่งที่ค้นหาแม้ว่าคำอธิบายจะบอกเกี่ยวกับความยาวผันแปร ส่วนใหญ่เราจะทราบความยาวล่วงหน้า
หากคุณทราบความยาวก่อนลงมือลองด้านล่าง:
char str1[1001] = { 0 };
fgets(str1, 1001, stdin); // 1000 chars may be read
แหล่งที่มา: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
ตามที่แนะนำคุณสามารถใช้ getchar () เพื่ออ่านจากคอนโซลได้จนกว่าจะส่งคืน end-of-line หรือ EOF โดยสร้างบัฟเฟอร์ของคุณเอง การเพิ่มบัฟเฟอร์แบบไดนามิกอาจเกิดขึ้นได้หากคุณไม่สามารถกำหนดขนาดเส้นสูงสุดที่เหมาะสมได้
คุณสามารถใช้ fgets เป็นวิธีที่ปลอดภัยในการรับบรรทัดเป็นสตริงที่สิ้นสุดด้วย C 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);
หากคุณใช้อินพุตคอนโซลหมดหรือหากการดำเนินการล้มเหลวด้วยเหตุผลบางประการ eof == NULL จะถูกส่งกลับและบัฟเฟอร์บรรทัดอาจไม่เปลี่ยนแปลง (ซึ่งเป็นเหตุผลว่าทำไมการตั้งค่า char ตัวแรกเป็น '\ 0' จึงมีประโยชน์)
fgets จะไม่เติมบรรทัด [] มากเกินไปและจะทำให้แน่ใจว่ามีค่าว่างหลังอักขระที่ยอมรับครั้งสุดท้ายในการส่งคืนที่สำเร็จ
หากถึงจุดสิ้นสุดของบรรทัดอักขระที่นำหน้า "\ 0" จะเป็น "\ n"
หากไม่มีการยุติ "\ n" ก่อนที่จะสิ้นสุด "\ 0" อาจเป็นไปได้ว่ามีข้อมูลเพิ่มเติมหรือคำขอถัดไปจะรายงานจุดสิ้นสุดของไฟล์ คุณจะต้องทำ fgets อื่นเพื่อดูว่าอันไหน (ในเรื่องนี้การวนลูปด้วย getchar () จะง่ายกว่า)
ในโค้ดตัวอย่าง (อัปเดต) ด้านบนถ้าบรรทัด [sizeof (line) -1] == '\ 0' หลังจากสร้าง fget สำเร็จคุณจะทราบว่าบัฟเฟอร์ถูกเติมเต็มแล้ว หากตำแหน่งนั้นดำเนินต่อไปโดย "\ n" คุณรู้ว่าคุณโชคดี มิฉะนั้นจะมีข้อมูลเพิ่มเติมหรือจุดสิ้นสุดของไฟล์อยู่ข้างหน้าใน stdin (เมื่อเติมบัฟเฟอร์ไม่สมบูรณ์คุณยังสามารถอยู่ที่จุดสิ้นสุดของไฟล์ได้และอาจไม่มี '\ n' ที่ท้ายบรรทัดปัจจุบันเนื่องจากคุณต้องสแกนสตริงเพื่อค้นหาและ / หรือกำจัด '\ n' ใด ๆ ก่อนสิ้นสุดสตริง ('\ 0' ตัวแรกในบัฟเฟอร์) ฉันชอบใช้ getchar () เป็นอันดับแรก)
ทำในสิ่งที่คุณต้องทำเพื่อจัดการกับมันยังคงมีบรรทัดมากกว่าจำนวนที่คุณอ่านเป็นชิ้นแรก ตัวอย่างของบัฟเฟอร์ที่เพิ่มขึ้นแบบไดนามิกสามารถทำให้ทำงานได้กับ getchar หรือ fgets มีกรณีขอบที่ยุ่งยากบางอย่างที่ต้องระวัง (เช่นอย่าลืมให้อินพุตถัดไปเริ่มจัดเก็บที่ตำแหน่งของ '\ 0' ที่สิ้นสุดอินพุตก่อนหน้าก่อนที่บัฟเฟอร์จะขยายออกไป)
จะอ่านบรรทัดจากคอนโซลใน C ได้อย่างไร?
การสร้างฟังก์ชันของคุณเองเป็นวิธีหนึ่งที่จะช่วยให้คุณอ่านบรรทัดจากคอนโซลได้สำเร็จ
ฉันใช้การจัดสรรหน่วยความจำแบบไดนามิกเพื่อจัดสรรจำนวนหน่วยความจำที่ต้องการที่ต้องการ
เมื่อเรากำลังจะหมดหน่วยความจำที่จัดสรรเราจะพยายามเพิ่มขนาดหน่วยความจำเป็นสองเท่า
และที่นี่ฉันใช้ลูปเพื่อสแกนอักขระแต่ละตัวของสตริงทีละตัวโดยใช้getchar()
ฟังก์ชันจนกว่าผู้ใช้จะป้อน'\n'
หรือEOF
อักขระ
ในที่สุดเราก็ลบหน่วยความจำที่จัดสรรเพิ่มเติมก่อนที่จะกลับบรรทัด
//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;
}
ตอนนี้คุณสามารถอ่านทั้งบรรทัดได้ดังนี้:
char *line = NULL;
line = scan_line(line);
นี่คือตัวอย่างโปรแกรมที่ใช้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
}
อินพุตตัวอย่าง:
Twinkle Twinkle little star.... in the sky!
ตัวอย่างผลลัพธ์:
Twinkle Twinkle little star.... in the sky!
ฉันเจอปัญหาเดียวกันเมื่อไม่นานมานี้นี่คือวิธีแก้ปัญหาของฉันหวังว่ามันจะช่วยได้
/*
* 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
เพื่อจัดการกรณีข้อผิดพลาด อย่างไรก็ตามคุณไม่คิดว่าจะสามารถนำกลับมาใช้ใหม่tmp_buf
ได้แทนที่จะใช้malloc
มันด้วยขนาดเดียวกันซ้ำแล้วซ้ำเล่า?
has_err
เพื่อรายงานข้อผิดพลาดทำให้ฟังก์ชันนี้ไม่ปลอดภัยและใช้งานน้อยกว่าความสะดวกสบาย อย่าทำแบบนั้น คุณระบุข้อผิดพลาดแล้วโดยส่งคืนค่า NULL นอกจากนี้ยังมีพื้นที่ที่จะคิดว่าข้อความแสดงข้อผิดพลาดที่พิมพ์ออกมาไม่ใช่ความคิดที่ดีในฟังก์ชันไลบรารีที่ใช้งานทั่วไป
ในระบบ 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
จะไม่เป็นโมฆะยกเลิกและมี\n
(หรือสิ่งที่แพลตฟอร์มของคุณใช้) ในท้ายที่สุด จะไม่ถูกต้องหลังจากการดำเนินการ I / O ถัดไปบนสตรีม
สิ่งนี้:
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;
}
วิธีที่ดีที่สุดและง่ายที่สุดในการอ่านบรรทัดจากคอนโซลคือการใช้ฟังก์ชัน getchar () ซึ่งคุณจะจัดเก็บทีละอักขระในอาร์เรย์
{
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 );
}
ฟังก์ชันนี้ควรทำในสิ่งที่คุณต้องการ:
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 );
ฉันหวังว่านี่จะช่วยได้.
fgets( buffer, sizeof(buffer), file );
เว้นที่ว่างไว้สำหรับการยุติโมฆะ sizeof(buffer)-1
fgets
while (!feof(file))
ผิดเสมอและนี่เป็นเพียงอีกหนึ่งตัวอย่างของการใช้งานที่ผิดพลาด