วิธีที่ง่ายที่สุดในการอ่านบรรทัดเต็มในโปรแกรมคอนโซล 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)-1fgets
                    while (!feof(file))ผิดเสมอและนี่เป็นเพียงอีกหนึ่งตัวอย่างของการใช้งานที่ผิดพลาด