ฉันควรจะใช้ fgets หรือ scanf กับถูกจำกัดข้อมูลใน c?

0

คำถาม

ฉันควรจะใช้ fgets หรือ formatted scanf เหมือน scanf("%10s", foo).

Excepted นั้น scanf ยังไม่ได้อ่านว่างเปล่าตัวอักษรที่จะแก้ไขแล้วก็ทำมากก stuffs กับ scanset แล้วทำไมฉันควรจะใช้ fgets แทนที่จะเป็น scanf?

ที่เขาแนะนำให้ผูกผ้ากันเปื้อนจะเป็นเกียรติอย่างยิ่ง


แก้ไข

อีกอย่างที่ฉันอยากจะถามคือ:ถึงแม้ตอนที่เราจะใช้ fgets สิ่งที่เกิดขึ้นถ้าผู้ใช้ป้องตัวอักษรมากกว่าขอบเขต(ฉันหมายถึงเยอะแยะตัวอักษร)มันำเพื่อนชนเลยล้นมาถึง? แล้ววิธีจัดการกับมัน?

c fgets input scanf
2021-11-23 13:53:00
4

คำตอบที่ดีที่สุด

5

ในที่สุดปฏิบัติการ sytems ผู้ใช้นำเข้าข้อมูลคือโดยปริยายสายหลัก. หนึ่งเหตุผลสำหรับนี่คือเพื่ออนุญาตให้ผู้ใช้ต้องกดปุ่มที่ใช้ปุ่มแบ็คสเปซเพื่อแก้ไขข้อมูลก่อนที่จะส่งข้อมูลให้โปรแกรม

สำหรับเส้นฐานของผู้ใช้นำเข้าข้อมูลมันก็แค่สุ่มและ intuitive สำหรับโปรแกรมอ่านหนึ่งเส้นของนำเข้าข้อมูลในเวลา. นี่คือสิ่งที่ฟังก์ชัน fgets มัน(ให้นั่งแถบชุดลวดลาย stencils ใหญ่พอที่จะจัดเก็บทั้งหมดเส้นของนำเข้าข้อมูล).

ฟังก์ชัน scanfอีกด้านนึง,ปกติแล้วยังไม่อ่านหนึ่งเส้นของนำเข้าข้อมูลในเวลา. ตัวอย่างเช่น,ตอนที่คุณใช้ %s หรือ %d การแปลงรูปแบบ specifier กับ scanfมันจะไม่เพียงพองทั้งหมดขอนำข้อมูลเข้าได้ แทนที่จะมันจะเดียวที่กินเวลามากนำเข้าข้อมูลที่ตรงกับการแปลงรูปแบบ specifier. นี่หมายความว่าคนบรรทัดใหม่ตัวละครในตอนจบของเส้นปกติแล้วจะไม่ถูกลือนกิน(ซึ่งได้นำอย่างง่ายดายเพื่อตั้งโปรแกรมแมลง). แล้ว scanf เรียกว่ากัน %d การแปลงรูปแบบ specifier จะพิจารณาข้อมูลอย่างเช่น 6sldf23dsfh2 ที่ใช้นำเข้าข้อมูลสำหรับจำนวน 6แต่ต่อไปเราทั้งคู่ก็จะลงเอเรีย scanf กับเดียวกัน specifier จะล้มเหลวเว้นแต่คุณต้องการยกเลิกการที่เหลือทั้งหมดของเส้นอกจากข้อมูลสายข้อมูลสื่อนี้

นี่พฤติกรรมของ scanf คือมันขัดกับความรู้สึกผแต่ทว่าพฤติกรรมของ fgets คือ intuitive ตอนที่กำลังเจอกับเส้นฐานของผู้ใช้นำเข้าข้อมูล.

หลังจากการใช้ fgetsคุณสามารถใช้ฟังก์ชัน sscanf บนข้อความสำหรับการวิเคราะห์เนื้อหาของการแบบส่วนบุคคลสาย นี่จะอนุญาตให้คุณทำต่อไปโดยใช้ scansets. หรือคุณสามารถวิเคราะห์สายตาคนอื่นหมายความว่าไง เหมือนกันทางตราบเท่าที่คุณใช้ fgets แทนที่จะเป็น scanf สำหรับอ่านข้อมูลคุณจะต้องจัดการเรื่องแสดงแค่บทเดียวของนำเข้าข้อมูลที่เป็นครั้งซึ่งเป็นธรรมชาติและ intuitive ทางที่จะจัดการกับเส้นฐานของผู้ใช้นำเข้าข้อมูล.

ตอนที่เราใช้ fgets สิ่งที่เกิดขึ้นถ้าผู้ใช้ป้องตัวอักษรมากกว่าขอบเขต(ฉันหมายถึงเยอะแยะตัวอักษร)มันำเพื่อนชนเลยล้นมาถึง? แล้ววิธีจัดการกับมัน?

ถ้าผู้ใช้เข้ามากกว่าตัวอักษรมากกว่าเหมาะอยู่ในบัฟเฟอร์ที่ระบุโดยผู้ที่สอง fgets ฟังก์ชันข้อโต้แย้งนั้นมันจะไม่เลยล้นมาถึงที่บัฟเฟอร์. แทนที่จะมันจะเพียงคลายแฟ้มอย่างที่หลายคนตัวจากการนำเข้าข้อมูลสายข้อมูลที่เหมาะอยู่ในแถบชุดลวดลาย stencils. คุณสามารถค้นหาความจริงว่าคนทั้งเส้นถูกอ่านโดยดูไม่ว่าข้อความมีส่วนบรรทัดใหม่ตัวตน '\n' ตอนที่ทุกอย่างจะจบลง

2021-11-23 15:13:39

ขอบคุณมากนะครับคุณคำตอบของคุณจริงๆช่วยฉันนะ
Becker

พฤติกรรมของ fgets()เป็นเพียง intuitive สำหรับ inputs นั่นไม่ได้นานกว่าที่คาดไว้เลย
supercat
2

นี่คือโดยปรกติจะคุยกันเรื่องเต็มไปด้วยความเห็นแต่น่าสนใจไม่น้อย ฉันสังเกตุการณ์ที่ใหญ่ส่วนใหญ่ของพวกนั้นต้องไปแล้วเขาตอบกลับมาที่คำถามที่คล้ายกันบนเว็บไซต์นี้ตกลงมาบนด้านข้างของ fgets(). ฉันเป็นหนึ่งในพวกนั้น ฉันเจอ fgets() จะดีขึ้นมากที่จะใช้สำหรับผู้ใช้นำเข้าข้อมูลมากกว่า scanf() กับสองสาข้อยกเว้น scanf() ถือว่าเป็นคนโดยหลายอย่างที่เป็น รายการย่อยของสิ่งที่เหมาะสมวิธีการจัดการเรื่องของผู้ใช้นำเข้าข้อมูล. ตัวอย่างเช่น

"มันจะบอกคุณว่ามันสำเร็จหรือล้มเหลวแต่สามารถบอกคุณแค่ประมาณอยู่ไหนมันล้มเหลวและไม่ใช่ทั้งหมยังไงหรือทำไม คุณ มีน้อยมากโอกาสที่จะทำอะไรผิดพลาดในการกู้คืน."
(jamesdlin). แต่ในตอนผลประโยชน์ของพยายามความสมดุลจะเริ่มจาก citing องมาถกเถียงกัน.

สำหรับผู้ใช้นำเข้าข้อมูลนั่นมาจาก stdin,i.e. ข้อมูลเข้าจากแป้นพิมพ์, fgets() จะเป็นทางเลือกที่ดีที่สุด มันเป็นมากกว่าให้อภัยใครในนั้นที่ ข้อความ มันอ่านว่าสามารถจะเต็ม validated ก่อนที่ การแปลงคือพยายาม

หนึ่งในไม่กี่ครั้งแล้วโดยใช้รูปแบบของ scanf(): fscanf() จะเป็นไรหรอกที่จะใช้อาจจะเป็นตอนที่กำลังทำการแปลงข้อมูลจากควบคุมแหล่งข่าวของ i.e. จากการอ่านเข้มงวด formatted องแฟ้มกับซ้ำอย่างอื่นฟิลด์

อีกอย่าง, งนี้เปรียบเทียบ ของที่สองสำคัญเพิ่มเติมนายได้เปรียบอะไรบ้างและ disadvantages ของทั้งสองอย่าง

แก้ไข:เพื่อที่อยู่ของปฏิบัติการเพิ่มเติมคำถามเรื่องเลยล้นมาถึง:

"อีกอย่างที่ฉันอยากจะถามคือ:ถึงแม้ตอนที่เราใช้ fgets อะไรเกิดขึ้นถ้าผู้ใช้ป้องตัวอักษรมากกว่าขอบเขต(ฉันหมายถึงเยอะ ตัวอักษร)มันำเพื่อนชนเลยล้นมาถึง? แล้ววิธีจัดการกับ คิดว่าคุณไม่เคยถามตัวเอง

[fgets()](https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm เป็นดีๆออกแบบเพื่อป้องกันชนเลยล้นมาถึงเองสามารถทำได้ง่ายๆโดยใช้พารามิเตอร์ของมันเหมาะสม,eg:

char buffer[100] = {0};
...
while fgets(buffer, sizeof buffer, stdin);  

นี่จะปกป้องนำเข้าข้อมูลมากกว่ากาดขนาดบัฟเฟอร์ข้อมูลจากการถูกประมวลผล,ดังนั้นกันที่เอ่อล้นออกมา.

ไม่ใช้ scanf()ป้องกันชนเอ่อล้นออกมาค่อนข้างตรงไปข้างหน้า:ใช้ ความกว้าง specifier ในรูปแบบข้อความ ถ้าคุณอยากจะอ่านข้อมูลสำหรับตัวอย่างและจำกัดข้อมูลขนาดจากผู้ใช้ที่จะ 100 ตัวอักษรแม็กซ์อดรหัสที่จะรวมต่อไปนี้:

char buffer[101] = {0};// includes space for 100 + 1 for NULL termination

scanf("%100s", buffer);
        ^^^  width specifier 

แต่ยังไงกับตัวเลข,เอ่อล้นออกมาคือไม่ค่อยดีโดยใช้ scanf(). สาธิตให้ดูอจะง่ายอย่างรหัส inputting สองคค่าชี้ให้เห็นว่าในความเห็นหนึ่งต่อไป:

int main(void)
{
    int val = 0;
    // test with 2147483647 & 2147483648
    scanf("%d", &val);
    printf("%d\n", val);
    
    return 0;
}

สำหรับคนที่สองค่าของระบบโยนต่อไปนี้:

NON-FATAL RUN-TIME ERROR: "test.c", line 11, col 5, thread id 22832: Function scanf: (errno == 34 [0x22]). Range error `

นี่คุณต้องอ่านอยู่ในข้อความแล้วทำตามกับข้อความให้จำนวนการแปลงโดยใช้หนึ่งของ strto_() ฟังก์ชัน: strtol(), strtod(),...). ทั้งคู่รวมความสามารถที่จะทดสอบสำหรับเอ่อล้นออกมา ก่อนที่ จะเกิดวิ่งเวลาแจ้งเตือนหรือเกิดข้อผิดพลาด. ควรจำไว้ว่าการใช้ atoi(), atod() ไม่ต้องการจะปกป้องจากเอ่อล้นออกมาเหมือนกัน

2021-11-23 14:10:20

ขอบคุณฉันขอบคุณมากสำหรับคำตอบของคุณ
Becker

เป็นคำถาม"เต็มไปด้วยความเห็น"? ผมต้องด้วยความเคารพนะครับไม่เห็นด้วย มันไม่ใช่เรื่องของความเห็นนั่น scanf มันเกือบจะหมดประโยขน์ที่ดีที่สุดสำหรับอ่านโสดง่ายอย่าง inputs อยู่เบื้อง-กับ-C โปรแกรมแต่ prohibitively นเรื่องยากที่จะทำอะไรที่เจนต่อโลกกำลังเรียนผ่านดาวเทียมละกัน—พวกนี้ชัดเจนข้อเท็จจริง! :-)
Steve Summit
1

ถึงตอนนี้ทุกคำตอบที่นี่เสนอที่ซับซ้อนของ scanf แล้ว fgetsแต่สิ่งที่ผมเชื่อว่ามันมีค่าพอที่จะพูดถึงเรื่องคืนนั้นทั้งสองของพวกนั้นฟังก์ชันเป็น deprecated อยู่ในปัจจุบันมาตรฐาน C. Scanf เป็นอันตรายโดยเฉพาะอย่างยิ่งเพราะว่ามันมีทุกประเภทรักษาความปลอดภัยปัญหาเรื่องกับแถบชุดลวดลาย stencils overflows. fgets ไม่เป็น problematic แต่จากประสบการณ์ของฉันมันดูจะนิดหน่อ clunky และไม่-แล้ว-มีประโยชน์ในการเรียน

ความจริงก็คือบ่อยครั้งคุณไม่รู้จริงๆว่านานแค่ไหนที่ของผู้ใช้นำเข้าข้อมูลจะเป็น คุณสามารถจะจัดการเรื่องนี้โดยการใช้ fgets กับ ฉันหวังว่าเรื่องนี้จะใหญ่พอแล้ว เว้นแต่ว่ามันไม่จริง ellegant. แทนที่จะเป็นสิ่งที่คุณบ่อยแค่ต้องการจะทำคือปรับอัตโนมัติแค่เพื่อที่จะเติบโตเป็นใหญ่พอที่จะซื้ออะไรก็ตามที่ของผู้ใช้นำเข้าข้อมูลจะส่งให้. และนี่คือตอนที่ getline ฟังก์ชันเข้ามาเล่น มันเป็นเคย อ่านมีจำนวนของตัวอักษรจากผู้ใช้จนกระทั่ง\n เป็นเจ. โดยพื้นฐาน,มันโหทั้งสายต้องของคุณความทรงจำตอนเป็นข้อความ

size_t getline(char **lineptr, size_t *n, FILE *stream);

ฟังก์ชันนี้ใช้ เป็น pointer ต้องเป็น dynamically กำข้อความ เป็นคนแรกจะได้เห็นกัน และ pointer ต้องเป็นขนาดของกำบัฟเฟอร์ เป็นที่สองเถียงกันและ สายข้อมูล ที่เป็นคนที่สามเถียงกันอยู่นะ (คุณจะเป็นสถานที่ stdin นั่นสำหรับบรรทัดคำสั่งนำเข้าข้อมูล). และจะได้ค่าจำนวนของอ่านตัวอักษรรวมถึง\n ในตอนจบแต่ไม่ใช่ terminating โพรโทคอล aimcomment.

ที่นี่คุณสามารถดูตัวอย่างเช่นการใช้พื้นทีี่ของมันฟังก์ชัน:

int main() {

printf("Input Something:\n");  // asking user for input

size_t length = 10;                   // creating "base" size of our buffer
char *input_string = malloc(length);  // allocating memory based on our initial buffer size
size_t length_read = getline(&input_string, &length, stdin);  // loading line from console to input_string
// now length_read contains how much characters we read
// and length contains new size of our buffer (if it changed during the getline execution)

printf("Characters read (including end of line but not null at the end)"
       ": %lu, current size of allocated buffer: %lu string: %s"
       , length_read, length, input_string);

free(input_string);    // like any other dynamically-allocated pointer, you must free it after usage
return 0;
}

แน่นอนโดยใช้ฟังก์ชันนี้ต้องการพื้นฐานความรู้เรื่องชี้แนะและปรับอัตโนมัติความทรงจำใน C ย่างไรก็ตามที่ให้มันฟังดูรื่มรมย์กว่านี้ซับซ้อนของธรรมชาติ getline คือคุ้มค่าอย่างแน่นอนมันเพราะของที่เตรียมไว้ให้ด้านล่างรปภและความยืดหยุ่น

คุณสามารถอ่านเกี่ยวกับฟังก์ชันนี้และอื่นๆข้อมูลฟังก์ชันที่มีอยู่ในซีบนเว็บไซต์: https://www.studymite.com/blog/strings-in-c ฉันเชื่อว่ามัน summarizes ที่ซับซ้อนของซีนำเข้าข้อมูลค่อนข้างดี

2021-11-23 19:18:00

ขอบคุณสำหรับคำแนะนำของคุณและการเชื่อมต่อมันจะช่วยฉันเยอะมาก
Becker
1

ถ้าคุณมีตัวอย่างเช่นตารางคู่ลำดับอักขระประกาศอย่าง

char s[100];

และอยากจะอ่านข้อความที่มีฝังช่องว่างนั้นคุณสามารถใช้ทั้ scanf วิธีดังต่อไปนี้:

scanf( "%99[^\n]", s );

หรือ fgets เหมือน:

fgets( s, sizeof( s ), stdin );

ความแตกต่างระหว่างสองคนนี้โทรมาคือเสียงเรียกของ scanf ยังไม่อ่านตัวใหม่แถวอักษร '\n' จากข้อมูลบัฟเฟอร์. ในขณะที่ fgets อ่านคนใหม่แถวอักษร '\n' ถ้ามีเพียงพอวกาศอยู่ในอักขระอาเรย์.

เพื่อลบคนใหม่แถวอักษร '\n' นั่นคือถูกจัดเก็บไว้ในตัวละครหลังจากใช้อาเรย์ fgets คุณสามารถเขียนสำหรับตัวอย่าง:

s[ strcspn( s, "\n" ) ] = '\0';

ถ้าข้อมูลข้อความมีมากกว่า 99 ตัวอักษรแล้วทั้งสองโทรอ่านได้อย่างเดียว 99 ตัวอักษรและต่อไปยังท้ายลำดับของตัวอักษรกับ terminating ศูนย์ตัวละคร '\0'. ทั้งหมดที่ตัวอักษรจะเป็นยังอยู่ในข้อมูลบัฟเฟอร์.

มีปัญหากับ fgets. ตัวอย่างเช่นถ้าก่อน fgets มันเคย scanf สำหรับตัวอย่าง:

scanf( "%d", &x );
fgets( s, sizeof( s ), stdin );

และผู้ใช้นำเข้าข้อมูลคือ:

10
Hello World

แล้วก็โทรหาของ fgets จะอ่านได้อย่างเดียวคนใหม่แถวอักษร '\n' นั่นคือถูกจัดเก็บไว้ในบัฟเฟอร์หลังจากกดดันที่ป้อนกุญแจเมื่อกค่าจำนวนเต็มในการโทรของ scanf เป็นอ่านแล้ว

ในกรณีนี้คุณต้องการจะเขียนรหัสนั้นจะถูกลบออกใหม่ขอสายอักขระ '\n' ก่อนที่โทรมา fgets.

คุณสามารถทำแบบนี้เพื่อตัวอย่างเช่นวิธีดังต่อไปนี้:

scanf( "%d", &x );
scanf( " " );
fgets( s, sizeof( s ), stdin );

ถ้าคุณกำลังใช้ scanf แล้วในเหตุการณ์นั้นมันเป็นคุณสามารถเขียน:

scanf( "%d", &x );
scanf( " %99[^\n]", s );
       ^^ 
2021-11-23 14:05:15

ในภาษาอื่นๆ

หน้านี้อยู่ในภาษาอื่นๆ

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................

ดังอยู่ในนี้หมวดหมู่

ดังคำถามอยู่ในนี้หมวดหมู่