ในคำตอบนี้ฉันจะคิดว่าคุณกำลังอ่านและการตีความบรรทัดของข้อความ บางทีคุณอาจแจ้งผู้ใช้ที่กำลังพิมพ์บางอย่างและกดปุ่ม RETURN หรือบางทีคุณกำลังอ่านบรรทัดข้อความที่มีโครงสร้างจากไฟล์ข้อมูลบางชนิด
เนื่องจากคุณกำลังอ่านบรรทัดข้อความคุณควรจัดระเบียบโค้ดของคุณรอบ ๆ ฟังก์ชั่นห้องสมุดที่อ่านได้และเป็นบรรทัดข้อความ ฟังก์ชั่นมาตรฐานคือfgets()
แม้ว่าจะมีคนอื่น ๆ (รวมถึงgetline
) จากนั้นขั้นตอนต่อไปคือการตีความบรรทัดข้อความนั้น
นี่คือสูตรพื้นฐานสำหรับการโทรfgets
เพื่ออ่านข้อความ:
char line[512];
printf("type something:\n");
fgets(line, 512, stdin);
printf("you typed: %s", line);
ข้อความนี้อ่านในบรรทัดเดียวแล้วพิมพ์ออกมา ตามที่เขียนไว้มีข้อ จำกัด สองสามข้อซึ่งเราจะไปได้ในไม่กี่นาที นอกจากนี้ยังมีคุณสมบัติที่ยอดเยี่ยมมาก: หมายเลข 512 ที่เราส่งผ่านเป็นอาร์กิวเมนต์ที่สองfgets
คือขนาดของอาร์เรย์ที่
line
เราขอfgets
ให้อ่าน ข้อเท็จจริงนี้ - ที่เราสามารถบอกได้fgets
ว่าได้รับอนุญาตให้อ่านมากแค่ไหน - หมายความว่าเรามั่นใจได้ว่าfgets
จะไม่ล้นอาร์เรย์โดยการอ่านมากเกินไป
ดังนั้นตอนนี้เรารู้วิธีอ่านบรรทัดข้อความ แต่ถ้าเราต้องการอ่านจำนวนเต็มหรือจำนวนเลขทศนิยมหรืออักขระเดี่ยวหรือคำเดียว? (นั่นคือสิ่งที่ถ้า
scanf
โทรที่เรากำลังพยายามที่จะปรับปรุงได้รับใช้ระบุรูปแบบเช่น%d
, %f
, %c
หรือ%s
?)
ง่ายต่อการตีความบรรทัดของข้อความ - สตริง - เป็นสิ่งเหล่านี้ การแปลงสตริงเป็นจำนวนเต็มที่ง่าย ( แต่ไม่สมบูรณ์) atoi()
วิธีการทำก็คือการโทร หากต้องการแปลงเป็นตัวเลขทศนิยมให้ทำatof()
ดังนี้ (และยังมีวิธีที่ดีกว่าดังที่เราจะเห็นในอีกสักครู่) นี่เป็นตัวอย่างง่ายๆ:
printf("type an integer:\n");
fgets(line, 512, stdin);
int i = atoi(line);
printf("type a floating-point number:\n");
fgets(line, 512, stdin);
float f = atof(line);
printf("you typed %d and %f\n", i, f);
หากคุณต้องการให้ผู้ใช้พิมพ์อักขระเดียว ( y
หรือ
อาจn
เป็นการตอบสนองแบบใช่ / ไม่ใช่) คุณสามารถคว้าตัวอักษรตัวแรกของบรรทัดเช่นนี้:
printf("type a character:\n");
fgets(line, 512, stdin);
char c = line[0];
printf("you typed %c\n", c);
(แน่นอนว่าสิ่งนี้ละเว้นความเป็นไปได้ที่ผู้ใช้พิมพ์การตอบสนองแบบหลายตัวอักษรโดยจะละเว้นอักขระพิเศษที่พิมพ์อย่างเงียบ ๆ )
ในที่สุดหากคุณต้องการให้ผู้ใช้พิมพ์สตริงแน่นอนไม่มีช่องว่างถ้าคุณต้องการที่จะปฏิบัติต่อสายอินพุต
hello world!
เป็นสตริง"hello"
ตามด้วยอย่างอื่น (ซึ่งเป็นscanf
รูปแบบที่%s
จะทำ) ดีในกรณีนั้นฉันเป็นใยเล็ก ๆ น้อย ๆ มันไม่ง่ายนักที่จะตีความบรรทัดใหม่ในลักษณะนั้นท้ายที่สุดดังนั้นคำตอบก็คือ ส่วนหนึ่งของคำถามจะต้องรอสักครู่
แต่ก่อนอื่นฉันต้องการย้อนกลับไปสามสิ่งที่ฉันข้ามไป
(1) เราโทรมา
fgets(line, 512, stdin);
เพื่ออ่านเข้าไปในอาเรline
ย์และโดยที่ 512 คือขนาดของอาเรย์line
จึงfgets
ไม่รู้ที่จะล้นมัน แต่เพื่อให้แน่ใจว่า 512 เป็นหมายเลขที่ถูกต้อง (โดยเฉพาะอย่างยิ่งเพื่อตรวจสอบว่ามีใครบางคน tweaked โปรแกรมเพื่อเปลี่ยนขนาด) คุณต้องอ่านกลับไปทุกที่ที่line
มีการประกาศ นั่นเป็นเรื่องน่ารำคาญดังนั้นจึงมีวิธีที่ดีกว่าสองวิธีในการซิงค์ขนาด คุณสามารถ (a) ใช้ตัวประมวลผลล่วงหน้าเพื่อสร้างชื่อสำหรับขนาด:
#define MAXLINE 512
char line[MAXLINE];
fgets(line, MAXLINE, stdin);
หรือ (b) ใช้sizeof
โอเปอเรเตอร์ของ C :
fgets(line, sizeof(line), stdin);
(2) ปัญหาที่สองคือเราไม่ได้ตรวจสอบข้อผิดพลาด เมื่อคุณกำลังอ่านอินพุตคุณควรเสมอตรวจสอบความเป็นไปได้ของข้อผิดพลาด หากเหตุผลใดก็ตามfgets
ไม่สามารถอ่านบรรทัดข้อความที่คุณขอให้มันแสดงถึงสิ่งนี้โดยส่งคืนพอยน์เตอร์พอยน์เตอร์ ดังนั้นเราควรทำสิ่งที่ชอบ
printf("type something:\n");
if(fgets(line, 512, stdin) == NULL) {
printf("Well, never mind, then.\n");
exit(1);
}
ในที่สุดก็มีปัญหาที่ต้องอ่านบรรทัดของข้อความ
fgets
อ่านตัวอักษรและเติมเข้าไปในอาร์เรย์ของคุณจนกว่าจะพบ\n
อักขระที่ยุติบรรทัดและเติม\n
อักขระลงในอาร์เรย์ของคุณเช่นกัน คุณสามารถดูสิ่งนี้หากคุณปรับเปลี่ยนตัวอย่างก่อนหน้าของเราเล็กน้อย:
printf("you typed: \"%s\"\n", line);
ถ้าฉันเรียกใช้และพิมพ์ "Steve" เมื่อมันแจ้งให้ฉันมันจะพิมพ์ออกมา
you typed: "Steve
"
ว่าในบรรทัดที่สองเป็นเพราะสตริงอ่านและพิมพ์ออกมาเป็นจริง"
"Steve\n"
บางครั้งการขึ้นบรรทัดใหม่ไม่สำคัญ (เช่นเมื่อเราโทรหา
atoi
หรือatof
เนื่องจากทั้งคู่ไม่สนใจการป้อนข้อมูลที่ไม่ใช่ตัวเลขพิเศษหลังจากตัวเลข) แต่บางครั้งมันก็สำคัญมาก บ่อยครั้งที่เราต้องการตัดการขึ้นบรรทัดใหม่นั้น มีหลายวิธีที่จะทำซึ่งฉันจะไปในนาที (ฉันรู้ว่าฉันพูดมาเยอะมาก แต่ฉันจะกลับไปที่สิ่งเหล่านั้นทั้งหมดฉันสัญญา)
เมื่อถึงจุดนี้คุณอาจกำลังคิดว่า: "ฉันคิดว่าคุณพูดว่าscanf
ไม่ดีและวิธีอื่นจะดีกว่านี้มาก แต่fgets
เริ่มมีอาการรำคาญการโทรscanf
เป็นเรื่องง่ายมาก ! "
แน่นอนว่าคุณสามารถใช้ต่อscanf
ไปได้หากต้องการ (และสำหรับ
สิ่งที่เรียบง่ายจริงๆในบางวิธีมันก็ง่ายกว่า) แต่โปรดอย่าร้องไห้ให้กับฉันเมื่อมันทำให้คุณล้มเหลวเนื่องจากหนึ่งใน 17 นิสัยใจคอและความผิดพลาดหรือเข้าสู่วงวนไม่สิ้นสุดเนื่องจากการป้อนข้อมูลของคุณ ไม่คาดหวังหรือเมื่อคุณไม่สามารถหาวิธีใช้เพื่อทำสิ่งที่ซับซ้อนมากขึ้น ลองดูที่fgets
จริงของความรำคาญ:
คุณต้องระบุขนาดอาร์เรย์เสมอ แน่นอนว่านั่นไม่ใช่เรื่องน่ารำคาญเลย - นั่นเป็นคุณสมบัติเพราะบัฟเฟอร์ล้นเป็นสิ่งเลวร้ายจริงๆ
คุณต้องตรวจสอบค่าส่งคืน ที่จริงแล้วนั่นคือการล้างเนื่องจากการใช้scanf
อย่างถูกต้องคุณต้องตรวจสอบค่าตอบแทนด้วย
คุณต้องถอด\n
หลังออก นี่คือฉันยอมรับว่าเป็นความรำคาญที่แท้จริง ฉันหวังว่าจะมีฟังก์ชั่นมาตรฐานที่ฉันสามารถชี้ให้คุณทราบว่าไม่มีปัญหาเล็กน้อยนี้ (โปรดไม่มีใครนำขึ้นมาgets
) แต่เมื่อเปรียบเทียบกับscanf's
สิ่งรบกวน 17 แบบที่แตกต่างกันฉันจะใช้สิ่งนี้สร้างความรำคาญfgets
ทุกวัน
ดังนั้นคุณจะตัดการขึ้นบรรทัดใหม่ได้อย่างไร สามวิธี:
(a) วิธีที่ชัดเจน:
char *p = strchr(line, '\n');
if(p != NULL) *p = '\0';
(b) วิธีที่ยุ่งยากและกะทัดรัด:
strtok(line, "\n");
น่าเสียดายที่อันนี้ไม่ได้ผลเสมอไป
(c) อีกวิธีหนึ่งที่กะทัดรัดและไม่ชัดเจน:
line[strcspn(line, "\n")] = '\0';
และตอนนี้ว่าที่ออกจากทางที่เราจะได้รับกลับไปเป็นอีกสิ่งหนึ่งที่ผมข้ามไป: ความไม่สมบูรณ์ของและatoi()
atof()
ปัญหาที่เกิดขึ้นกับสิ่งเหล่านี้คือพวกเขาไม่ได้ให้การบ่งชี้ที่เป็นประโยชน์ใด ๆ ถึงความสำเร็จของความสำเร็จหรือความล้มเหลว: พวกเขาละเว้นการป้อนข้อมูลที่ไม่ใช่ตัวเลขต่อท้ายและพวกเขาจะคืนค่า 0 เป็นเงียบ ๆ หากไม่มีการป้อนตัวเลข ทางเลือกที่แนะนำ - ซึ่งยังมีข้อดีอื่น ๆ บางอย่าง - มีและ
strtol
ยังช่วยให้คุณใช้ฐานอื่นที่ไม่ใช่ 10 ซึ่งหมายความว่าคุณจะได้รับผลกระทบจาก (เหนือสิ่งอื่นใด) หรือด้วยstrtod
strtol
%o
%x
scanf
. แต่การแสดงวิธีการใช้ฟังก์ชั่นเหล่านี้อย่างถูกต้องนั้นเป็นเรื่องราวในตัวเองและจะทำให้เสียสมาธิมากเกินไปจากสิ่งที่เปลี่ยนไปแล้วในการเล่าเรื่องที่ค่อนข้างกระจัดกระจายดังนั้นฉันจะไม่พูดอะไรเกี่ยวกับพวกเขาอีกเลย
ข้อมูลที่เหลือเกี่ยวกับการบรรยายหลักที่เหลือคุณอาจพยายามวิเคราะห์ว่ามันซับซ้อนกว่าตัวเลขหรือตัวละครเพียงตัวเดียว ถ้าคุณต้องการอ่านบรรทัดที่มีตัวเลขสองตัวหรือคำที่คั่นด้วยช่องว่างหลายคำหรือเครื่องหมายวรรคตอนกำหนดกรอบเฉพาะ นั่นคือสิ่งที่น่าสนใจและสิ่งที่อาจจะซับซ้อนหากคุณพยายามทำสิ่งต่าง ๆ โดยใช้scanf
และที่มีตัวเลือกมากมายตอนนี้คุณได้อ่านข้อความหนึ่งบรรทัดโดยใช้อย่างหมดจดfgets
แม้ว่าเรื่องราวทั้งหมดในตัวเลือกเหล่านั้นทั้งหมด อาจเติมหนังสือได้ดังนั้นเราจะทำได้แค่ขูดผิวที่นี่
เทคนิคที่ฉันโปรดปรานคือการแบ่งบรรทัดออกเป็น "คำ" ที่คั่นด้วยช่องว่างจากนั้นทำอะไรเพิ่มเติมกับ "คำ" แต่ละคำ หนึ่งฟังก์ชันหลักที่เป็นมาตรฐานสำหรับการทำเช่นนี้คือ
strtok
(ซึ่งก็มีปัญหาและมีอัตราการอภิปรายแยกต่างหากทั้งหมด) การตั้งค่าของตัวเองเป็นฟังก์ชั่นเฉพาะสำหรับการสร้างอาร์เรย์ของตัวชี้ไปที่แต่ละหักออกจากกัน "คำว่า" ฟังก์ชั่นผมอธิบายใน
บันทึกหลักสูตรเหล่านี้ เมื่อใดก็ตามที่คุณมีคำว่า "word" คุณจะสามารถดำเนินการแต่ละอย่างได้ต่อไปอาจจะมีatoi
/ atof
/ strtol
/ strtod
ฟังก์ชั่นเดียวกันที่เราได้ดูไปแล้ว
ขัดแย้งแม้ว่าเราจะได้รับค่าใช้จ่ายเป็นจำนวนเงินที่ยุติธรรมของเวลาและความพยายามที่นี่หาวิธีที่จะย้ายออกไปจากscanf
วิธีที่ดีอีกที่จะจัดการกับบรรทัดของข้อความที่เราเพิ่งอ่านด้วย
คือการผ่านไปfgets
sscanf
ด้วยวิธีนี้คุณจะได้ข้อได้เปรียบscanf
เกือบทั้งหมด แต่ไม่มีข้อเสียส่วนใหญ่
หากไวยากรณ์อินพุตของคุณซับซ้อนเป็นพิเศษคุณอาจใช้ไลบรารี่ "regexp" เพื่อแยกวิเคราะห์
สุดท้ายคุณสามารถใช้โซลูชันแยกวิเคราะห์เฉพาะกิจที่เหมาะกับคุณ คุณสามารถเลื่อนอักขระทีละบรรทัดโดยใช้
char *
ตัวชี้ตรวจสอบอักขระที่คุณต้องการ หรือคุณสามารถค้นหาตัวอักษรที่เฉพาะเจาะจงโดยใช้ฟังก์ชั่นเช่นstrchr
หรือstrrchr
หรือstrspn
หรือหรือstrcspn
strpbrk
หรือคุณสามารถแยก / แปลงและข้ามกลุ่มอักขระตัวเลขโดยใช้strtol
หรือ
strtod
ฟังก์ชั่นที่เราข้ามไปก่อนหน้านี้
เห็นได้ชัดว่ามีอีกมากมายที่สามารถพูดได้ แต่หวังว่าการแนะนำนี้จะช่วยให้คุณเริ่มต้นได้
(r = sscanf("1 2 junk", "%d%d", &x, &y)) != 2
ไม่สามารถตรวจจับข้อความที่ไม่ใช่ตัวเลขต่อท้ายได้ไม่ดี