เหตุใดไฟล์ข้อความควรลงท้ายด้วยการขึ้นบรรทัดใหม่


1467

ฉันถือว่าทุกคนที่นี่คุ้นเคยกับสุภาษิตว่าไฟล์ข้อความทั้งหมดควรลงท้ายด้วยบรรทัดใหม่ ฉันรู้จักกฎนี้มาหลายปีแล้ว แต่ฉันก็สงสัยอยู่เสมอว่าทำไม


30
แค่ nitpick ไม่ใช่ "บรรทัดใหม่" ที่ท้ายไฟล์ มันคือ "ตัวแบ่งบรรทัด" ที่ท้ายบรรทัดสุดท้าย ดูคำตอบที่ดีที่สุดสำหรับคำถามที่เกี่ยวข้อง: stackoverflow.com/questions/16222530/…
gcb

346
เพียงเพื่อเพิ่มเติม nitpick เขาไม่ได้เขียน“ บรรทัดใหม่” จริง ๆ เขาเขียน“ ขึ้นบรรทัดใหม่” ซึ่งถูกต้อง
sindrenm

5
ไม่คุ้นเคย แต่สงสัยว่าฉันเป็นจริงเพราะจำนวนกรณีที่ newline ฟุ่มเฟือยจริง ๆ แล้วทำลายสิ่งต่าง ๆ สูงเกินไปสำหรับรสนิยมของฉัน
เล็กน้อย

2
ขณะนี้ฉันกำลังใช้ Node.js สตรีมเพื่อแยกวิเคราะห์ข้อมูลข้อความบรรทัดต่อบรรทัดและการขาดการแบ่งบรรทัดเทอร์มินัลน่ารำคาญเนื่องจากฉันต้องเพิ่มตรรกะพิเศษเมื่อด้านอินพุตของสตรีมเสร็จสิ้น / ปิดเพื่อให้แน่ใจว่าบรรทัดสุดท้ายจะได้รับการประมวลผล
Mark K Cowan

23
วิธี Unix นับถือพฤติกรรมทั่วไปในตอนท้ายของไฟล์จะเป็นดังนี้: \ n ตัวอักษรไม่ได้เริ่มต้นสาย; แทนพวกเขาจบพวกเขา ดังนั้น \ n เป็นตัวคั่นบรรทัดไม่ใช่ตัวคั่นบรรทัด บรรทัดแรก (เหมือนทุกบรรทัด) ไม่จำเป็นต้อง \ n เพื่อเริ่มต้น บรรทัดสุดท้าย (เหมือนทุกบรรทัด) ต้องการ \ n เพื่อสิ้นสุด \ n ที่ส่วนท้ายของไฟล์ไม่ได้สร้างบรรทัดเพิ่มเติม อย่างไรก็ตามบางครั้งโปรแกรมแก้ไขข้อความจะเพิ่มบรรทัดว่างที่มองเห็นได้ที่นั่น แม้แต่ emacs ก็เลือกทำได้
MarkDBlackwell

คำตอบ:


1381

เพราะนั่นเป็นวิธีที่มาตรฐาน POSIX กำหนดบรรทัด :

3.206 สาย
ลำดับของอักขระที่ไม่ใช่ <newline> ที่เป็นศูนย์หรือมากกว่ารวมทั้งอักขระ <newline> ที่ยกเลิก

ดังนั้นบรรทัดที่ไม่ลงท้ายด้วยอักขระขึ้นบรรทัดใหม่จึงไม่ถือว่าเป็นบรรทัดที่แท้จริง นั่นเป็นสาเหตุที่บางโปรแกรมมีปัญหาในการประมวลผลบรรทัดสุดท้ายของไฟล์หากไม่ได้ขึ้นบรรทัดใหม่

มีข้อได้เปรียบอย่างน้อยหนึ่งข้อในแนวทางนี้เมื่อทำงานกับเทอร์มินัลอีมูเลเตอร์: เครื่องมือทั้งหมดของ Unix คาดว่าการประชุมนี้และทำงานกับมัน ตัวอย่างเช่นเมื่อเชื่อมไฟล์เข้าด้วยcatกันไฟล์ที่ถูกยกเลิกโดยการขึ้นบรรทัดใหม่จะมีเอฟเฟกต์ที่แตกต่างจากที่ไม่มี:

$ more a.txt
foo
$ more b.txt
bar$ more c.txt
baz
$ cat {a,b,c}.txt
foo
barbaz

และตามตัวอย่างก่อนหน้านี้ยังแสดงให้เห็นเมื่อแสดงไฟล์บนบรรทัดคำสั่ง (เช่นผ่านmore) ไฟล์ที่ถูกยกเลิกการขึ้นบรรทัดใหม่จะแสดงผลลัพธ์ที่ถูกต้อง ไฟล์ที่ถูกยกเลิกอย่างไม่ถูกต้องอาจถูกอ่านไม่ออก (บรรทัดที่สอง)

เพื่อความสอดคล้องจะเป็นประโยชน์อย่างมากในการทำตามกฎนี้ - มิฉะนั้นการทำเช่นนี้จะเกิดขึ้นเป็นพิเศษเมื่อต้องรับมือกับเครื่องมือ Unix ที่เป็นค่าเริ่มต้น


คิดให้แตกต่าง: ถ้าบรรทัดไม่ถูกยกเลิกด้วยการขึ้นบรรทัดใหม่การทำคำสั่งเช่นcatมีประโยชน์นั้นยากกว่ามากคุณจะสร้างคำสั่งเพื่อเชื่อมไฟล์ต่าง ๆ ได้อย่างไร

  1. มันทำให้การเริ่มต้นของแต่ละไฟล์บนบรรทัดใหม่ซึ่งเป็นสิ่งที่คุณต้องการ 95% ของเวลา; แต่
  2. อนุญาตให้รวมบรรทัดสุดท้ายและบรรทัดแรกของสองไฟล์ดังตัวอย่างในระหว่างb.txtและc.txt?

แน่นอนว่าสิ่งนี้สามารถแก้ไขได้แต่คุณจำเป็นต้องใช้catความซับซ้อนมากขึ้น (โดยการเพิ่มอาร์กิวเมนต์บรรทัดคำสั่งตำแหน่งเช่นcat a.txt --no-newline b.txt c.txt) และตอนนี้คำสั่งมากกว่าแต่ละไฟล์แต่ละไฟล์จะควบคุมวิธีการวางร่วมกันกับไฟล์อื่น ๆ นี่เกือบจะไม่สะดวกอย่างแน่นอน

... หรือคุณต้องแนะนำตัวละครแมวมองพิเศษเพื่อทำเครื่องหมายบรรทัดที่ควรจะต่อเนื่องแทนที่จะถูกยกเลิก ทีนี้คุณก็ติดอยู่กับสถานการณ์เช่นเดียวกับ POSIX ยกเว้นการกลับด้าน (การต่อบรรทัดมากกว่าตัวอักขระการยกเลิกบรรทัด)


ตอนนี้ในระบบที่ไม่รองรับ POSIX (ทุกวันนี้ส่วนใหญ่เป็น Windows) ประเด็นคือ moot: โดยทั่วไปไฟล์จะไม่ลงท้ายด้วยการขึ้นบรรทัดใหม่และการนิยาม (ไม่เป็นทางการ) ของบรรทัดอาจเป็น "ข้อความที่คั่นด้วยการขึ้นบรรทัดใหม่" (สังเกตความสำคัญ) สิ่งนี้ถูกต้องทั้งหมด อย่างไรก็ตามสำหรับข้อมูลที่มีโครงสร้าง (เช่นโค้ดโปรแกรม) มันทำให้การแยกวิเคราะห์มีความซับซ้อนน้อยที่สุด: โดยทั่วไปหมายความว่าต้องมีการแยกวิเคราะห์ใหม่ ถ้า parser นั้นถูกเขียนด้วยนิยาม POSIX ในใจคุณอาจแก้ไขโทเค็นสตรีมได้ง่ายกว่าตัวแยกวิเคราะห์ - กล่าวอีกนัยหนึ่งให้เพิ่มโทเค็น "newline บรรทัดใหม่" ที่ส่วนท้ายของอินพุต


9
แม้ว่าตอนนี้จะค่อนข้างไม่สามารถแก้ไขได้อย่างชัดเจน POSIX ทำผิดพลาดเมื่อกำหนดบรรทัด - เป็นหลักฐานตามจำนวนคำถามที่เกี่ยวข้องกับปัญหานี้ บรรทัดควรถูกกำหนดเป็นศูนย์หรือมากกว่าตัวอักษรยกเลิกโดย <eol>, <eof> หรือ <eol> <eof> เครื่องมือแยกวิเคราะห์ความซับซ้อนไม่ใช่ข้อกังวลที่ถูกต้อง ความซับซ้อนหากเป็นไปได้ควรย้ายจากหัวโปรแกรมเมอร์และไปยังห้องสมุด
Doug Coburn

23
@DougCoburn คำตอบนี้เคยมีการอภิปรายทางเทคนิคที่ละเอียดถี่ถ้วนอธิบายว่าทำไมมันถึงผิดและทำไม POSIX ถึงทำสิ่งที่ถูกต้อง น่าเสียดายที่ความคิดเห็นเหล่านี้เพิ่งถูกลบโดยผู้ดูแลที่มีปัญหา สั้น ๆ มันไม่เกี่ยวกับการแยกวิเคราะห์ความซับซ้อน แต่คำจำกัดความของคุณทำให้เครื่องมือผู้เขียนเช่นcatในทางที่มีประโยชน์และสอดคล้องกันมากขึ้น
Konrad Rudolph

8
@Leon กฎ POSIX เป็นข้อมูลเกี่ยวกับการลดขนาดของเคส และมันก็ทำอย่างสวยงาม จริง ๆ แล้วฉันรู้สึกว่าผู้คนไม่เข้าใจสิ่งนี้: มันเป็นคำนิยามที่ง่ายที่สุด
Konrad Rudolph

6
@ BT ฉันคิดว่าคุณสมมติว่าตัวอย่างเวิร์กโฟลว์ของฉันสะดวกกว่าคือเหตุผลที่อยู่เบื้องหลังการตัดสินใจ ไม่มันเป็นเพียงผลที่ตามมา เหตุผลก็คือว่ากฎ POSIX คือกฎที่ง่ายที่สุดและซึ่งจะทำให้สายการจัดการในการแยกวิเคราะห์ที่ง่ายที่สุด เหตุผลเดียวที่เรามีการถกเถียงกันก็คือ Windows ทำแตกต่างกันและนั่นก็คือมีเครื่องมือมากมายที่ล้มเหลวในไฟล์ POSIX หากทุกคนทำ POSIX จะไม่มีปัญหาใด ๆ แต่คนบ่นเกี่ยวกับ POSIX ไม่ใช่เกี่ยวกับ Windows
Konrad Rudolph

7
@ BT ฉันแค่อ้างถึง Windows เพื่อชี้ให้เห็นกรณีที่กฎ POSIX ไม่สมเหตุสมผล ฉันมีความสุขมากกว่าที่จะไม่พูดถึงมันในการสนทนานี้อีกครั้ง แต่การอ้างสิทธิ์ของคุณก็สมเหตุสมผลน้อยลง: บนแพลตฟอร์ม POSIX มันไม่มีเหตุผลที่จะพูดถึงไฟล์ข้อความที่มีแบบแผนการสิ้นสุดบรรทัดที่แตกต่างกันเนื่องจากไม่มีเหตุผลที่จะสร้างมัน ข้อดีคืออะไร ไม่มีเลย - สรุปผมจริงๆไม่เข้าใจความเกลียดชังคำตอบนี้ (หรือกฎ POSIX) เป็น Engendering การพูดอย่างตรงไปตรงมามันไร้เหตุผลอย่างสมบูรณ์
Konrad Rudolph

282

แต่ละบรรทัดควรถูกยกเลิกด้วยอักขระขึ้นบรรทัดใหม่รวมถึงบรรทัดสุดท้าย บางโปรแกรมมีปัญหาในการประมวลผลบรรทัดสุดท้ายของไฟล์หากไม่ได้ขึ้นบรรทัดใหม่

GCC เตือนว่าไม่ใช่เพราะไม่สามารถประมวลผลไฟล์ได้ แต่เนื่องจากต้องเป็นส่วนหนึ่งของมาตรฐาน

มาตรฐานภาษา C บอกว่าไฟล์ต้นฉบับที่ไม่ว่างจะลงท้ายด้วยอักขระบรรทัดใหม่ซึ่งจะไม่นำหน้าด้วยอักขระแบ็กสแลชทันที

เนื่องจากนี่เป็นประโยค "จะ" เราจะต้องส่งข้อความวินิจฉัยสำหรับการละเมิดกฎนี้

นี่คือในส่วน 2.1.1.2 ของมาตรฐาน ANSI C 1989 ส่วน 5.1.1.2 ของมาตรฐาน ISO C 1999 (และอาจเป็นมาตรฐาน ISO C 1990 ด้วย)

อ้างอิง: จีซี / เก็บจดหมาย


17
โปรดเขียนโปรแกรมที่ดีจากนั้นอนุญาตให้แทรกบรรทัดใหม่ที่จำเป็นขณะประมวลผลหรือสามารถจัดการกับรายการ "หายไป" ได้ ... ซึ่งอันที่จริงแล้วไม่หายไป
tobibeer

4
@BilltheLizard, อะไรคือตัวอย่างบางส่วนของ"บางโปรแกรมมีปัญหาการประมวลผลบรรทัดสุดท้ายของไฟล์หากยังไม่ได้ยกเลิกการขึ้นบรรทัดใหม่" ?
Pacerier

4
@Pacerier wc -lจะไม่นับบรรทัดสุดท้ายของไฟล์หากไม่ได้ขึ้นบรรทัดใหม่ นอกจากนี้catจะเข้าร่วมบรรทัดสุดท้ายของไฟล์โดยมีบรรทัดแรกของไฟล์ถัดไปเป็นไฟล์เดียวหากบรรทัดสุดท้ายของไฟล์แรกไม่ได้ขึ้นบรรทัดใหม่ โปรแกรมใด ๆ ที่กำลังมองหาบรรทัดใหม่มากในฐานะตัวคั่นมีศักยภาพที่จะทำให้เกิดปัญหาขึ้น
Bill the Lizard

2
@BilltheLizard ผมหมายถึงwcได้รับแล้วกล่าวถึง ....
Pacerier

2
@BilltheLizard, แย่มาก, ที่จะอธิบาย: อะไรคือตัวอย่างของโปรแกรมที่มีปัญหาในการประมวลผลบรรทัดสุดท้ายของไฟล์ถ้ามันไม่ได้ขึ้นบรรทัดใหม่ (นอกเหนือจากที่มีการกล่าวถึงจำนวนมากในเธรดcatและwc)
Pacerier

116

คำตอบนี้เป็นความพยายามในการตอบคำถามทางเทคนิคมากกว่าความเห็น

ถ้าเราต้องการที่จะเป็น POSIX purists เรากำหนดบรรทัดเป็น:

ลำดับของอักขระที่ไม่ใช่ <newline> ที่เป็นศูนย์หรือมากกว่ารวมทั้งอักขระ <newline> ที่ยกเลิก

ที่มา: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206

บรรทัดที่ไม่สมบูรณ์เป็น:

ลำดับของอักขระที่ไม่ใช่ <newline> ตั้งแต่หนึ่งตัวขึ้นไปที่ท้ายไฟล์

ที่มา: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195

ไฟล์ข้อความเป็น:

ไฟล์ที่มีตัวอักษรจัดเป็นศูนย์หรือมากกว่าบรรทัด บรรทัดไม่มีอักขระ NUL และไม่มีความยาวเกิน {LINE_MAX} ไบต์รวมถึงอักขระ <newline> แม้ว่า POSIX.1-2008 จะไม่แยกความแตกต่างระหว่างไฟล์ข้อความและไฟล์ไบนารี (ดูมาตรฐาน ISO C) แต่ยูทิลิตี้จำนวนมากจะสร้างเอาต์พุตที่สามารถคาดการณ์ได้หรือมีความหมายเมื่อทำงานกับไฟล์ข้อความ ยูทิลิตี้มาตรฐานที่มีข้อ จำกัด ดังกล่าวจะระบุ "ไฟล์ข้อความ" ในส่วน STDIN หรือ INPUT FILES เสมอ

ที่มา: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_397

สตริงเป็น:

ลำดับที่ต่อเนื่องกันของไบต์ถูกยกเลิกโดยและรวมถึงไบต์แรกที่ว่าง

ที่มา: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396

จากนี้เราสามารถได้รับว่าครั้งเดียวที่เราอาจพบปัญหาประเภทใดคือถ้าเราจัดการกับแนวคิดของบรรทัดของไฟล์หรือไฟล์เป็นไฟล์ข้อความ (เป็นไฟล์ข้อความที่เป็นองค์กรของศูนย์ หรือมากกว่านั้นและบรรทัดที่เรารู้ว่าต้องจบด้วย <newline>)

กรณีในจุด: wc -l filename.

จากwcคู่มือของเราอ่าน:

บรรทัดถูกกำหนดเป็นสตริงของอักขระคั่นด้วยอักขระ <newline>

แล้วความหมายของไฟล์ JavaScript, HTML และ CSS คือ อะไรเป็นไฟล์ข้อความ ?

ในเบราว์เซอร์ IDEs ที่ทันสมัยและแอปพลิเคชันส่วนหน้าอื่น ๆ ไม่มีปัญหาในการข้าม EOL ที่ EOF แอปพลิเคชั่นจะวิเคราะห์ไฟล์อย่างถูกต้อง เนื่องจากไม่ใช่ว่าระบบปฏิบัติการทั้งหมดจะเป็นไปตามมาตรฐาน POSIX ดังนั้นจึงเป็นไปไม่ได้สำหรับเครื่องมือที่ไม่ใช่ระบบปฏิบัติการ (เช่นเบราว์เซอร์) เพื่อจัดการไฟล์ตามมาตรฐาน POSIX (หรือมาตรฐานระดับ OS ใด ๆ )

ด้วยเหตุนี้เราจึงค่อนข้างมั่นใจได้ว่า EOL ที่ EOF จะไม่มีผลกระทบเชิงลบต่อระดับแอปพลิเคชันโดยไม่คำนึงว่าจะทำงานบนระบบปฏิบัติการ UNIX หรือไม่

ณ จุดนี้เราสามารถพูดได้อย่างมั่นใจว่าการข้าม EOL ที่ EOF นั้นปลอดภัยเมื่อจัดการกับ JS, HTML, CSS ที่ฝั่งไคลเอ็นต์ ที่จริงแล้วเราสามารถระบุว่าการลดไฟล์ใดไฟล์หนึ่งโดยไม่มี <newline> นั้นปลอดภัย

เราสามารถก้าวไปอีกขั้นหนึ่งและบอกว่าเท่าที่ NodeJS เกี่ยวข้องมันก็ไม่สามารถปฏิบัติตามมาตรฐาน POSIX เพราะมันสามารถทำงานในสภาพแวดล้อมที่ไม่สอดคล้องกับ POSIX

เราจะเหลืออะไรอีกแล้ว เครื่องมือระดับระบบ

นี่หมายถึงปัญหาเฉพาะที่อาจเกิดขึ้นกับเครื่องมือที่ใช้ความพยายามในการปฏิบัติหน้าที่ตามความหมายของ POSIX (เช่นคำจำกัดความของบรรทัดดังแสดงในwc)

แม้กระนั้นเชลล์บางตัวก็จะไม่ติด POSIX โดยอัตโนมัติ ยกตัวอย่างเช่นการทุบตีไม่ได้เริ่มต้นกับพฤติกรรม POSIX POSIXLY_CORRECTมีสวิทช์ที่จะเปิดใช้งานได้คือ:

อาหารสำหรับความคิดเกี่ยวกับคุณค่าของ EOL ว่าเป็น <newline>: https://www.rfc-editor.org/old/EOLstory.txt

อยู่ในเส้นทางการขับรถสำหรับทุกความตั้งใจและจุดประสงค์ในการใช้งานลองพิจารณาสิ่งนี้:

มาทำงานกับไฟล์ที่ไม่มี EOL ในการเขียนไฟล์ในตัวอย่างนี้เป็น JavaScript แบบย่อที่ไม่มี EOL

curl http://cdnjs.cloudflare.com/ajax/libs/AniJS/0.5.0/anijs-min.js -o x.js
curl http://cdnjs.cloudflare.com/ajax/libs/AniJS/0.5.0/anijs-min.js -o y.js

$ cat x.js y.js > z.js

-rw-r--r--  1 milanadamovsky   7905 Aug 14 23:17 x.js
-rw-r--r--  1 milanadamovsky   7905 Aug 14 23:17 y.js
-rw-r--r--  1 milanadamovsky  15810 Aug 14 23:18 z.js

สังเกตcatขนาดของไฟล์ว่าเป็นผลรวมของแต่ละส่วน หากการต่อไฟล์ JavaScript เป็นข้อกังวลสำหรับไฟล์ JS ข้อกังวลที่เหมาะสมกว่าคือการเริ่มต้นไฟล์ JavaScript แต่ละไฟล์ด้วยเซมิโคลอน

อย่างที่คนอื่นพูดถึงในเธรดนี้: ถ้าคุณต้องการcatไฟล์สองไฟล์ที่เอาต์พุตกลายเป็นหนึ่งบรรทัดแทนที่จะเป็นสองไฟล์ พูดอีกอย่างคือcatทำในสิ่งที่ควรทำ

manของcatเพียงกล่าวถึงการอ่านการป้อนข้อมูลถึง EOF ไม่ <newline> โปรดทราบว่า-nสวิตช์ของcatจะพิมพ์บรรทัดที่ไม่ใช่ <newline> ที่สิ้นสุด (หรือบรรทัดที่ไม่สมบูรณ์ ) เป็นบรรทัดโดยที่จำนวนเริ่มต้นที่1 (ตามที่man.)

-n กำหนดจำนวนบรรทัดเอาต์พุตโดยเริ่มต้นที่ 1

เมื่อเราเข้าใจวิธีที่ POSIX กำหนดบรรทัดแล้วพฤติกรรมนี้จะคลุมเครือหรือไม่เข้ากันได้

การทำความเข้าใจกับวัตถุประสงค์และการปฏิบัติตามเครื่องมือที่กำหนดจะช่วยในการกำหนดความสำคัญของการสิ้นสุดไฟล์ด้วย EOL ใน C, C ++, Java (JARs) ฯลฯ ... มาตรฐานบางอย่างจะกำหนดบรรทัดใหม่เพื่อความถูกต้อง - ไม่มีมาตรฐานดังกล่าวสำหรับ JS, HTML, CSS

ตัวอย่างเช่นแทนที่จะใช้wc -l filenameอย่างใดอย่างหนึ่งสามารถทำได้awk '{x++}END{ print x}' filenameและมั่นใจได้ว่าความสำเร็จของงานนั้นไม่ได้รับอันตรายจากไฟล์ที่เราอาจต้องการดำเนินการที่เราไม่ได้เขียน (เช่นห้องสมุดบุคคลที่สามเช่น minified JS เราcurld) - เว้นแต่เรา ความตั้งใจที่แท้จริงคือการนับบรรทัดในความหมายที่สอดคล้องกับ POSIX

ข้อสรุป

จะมีกรณีการใช้งานจริงน้อยมากที่การข้าม EOL ที่ EOF สำหรับไฟล์ข้อความบางอย่างเช่น JS, HTML และ CSS จะมีผลกระทบด้านลบ - ถ้าเป็นเช่นนั้น หากเราพึ่งพา <newline> แสดงตนเรากำลังจำกัดความน่าเชื่อถือของเครื่องมือของเราเฉพาะกับไฟล์ที่เราสร้างและเปิดตัวเราเองจนถึงข้อผิดพลาดที่อาจเกิดขึ้นจากไฟล์ของบุคคลที่สาม

คุณธรรมของเรื่องราว: เครื่องมือช่างที่ไม่มีจุดอ่อนในการพึ่งพา EOL ที่ EOF

รู้สึกฟรีเพื่อโพสต์กรณีการใช้งานเนื่องจากพวกเขาใช้กับ JS, HTML และ CSS ที่เราสามารถตรวจสอบว่าการข้าม EOL มีผลกระทบอย่างไร


2
POSIX ไม่ได้ติดแท็กในคำถาม ... สิ่งที่เกี่ยวกับการสิ้นสุดสาย MVS / OS? หรือจุดสิ้นสุดของบรรทัด MS-DOS? อย่างไรก็ตามระบบ posix ที่รู้จักทั้งหมดอนุญาตให้ใช้ไฟล์ข้อความโดยไม่ต้องลงท้ายบรรทัดสุดท้าย (ไม่พบกรณีของระบบการเรียกร้องที่เป็นไปตามมาตรฐาน posix ซึ่ง "text file" มีการดูแลเป็นพิเศษในเคอร์เนลเพื่อแทรก newline ที่เหมาะสมในกรณีที่ไม่มี มัน)
Luis Colorado

62

มันอาจจะเกี่ยวข้องกับความแตกต่างระหว่าง :

  • ไฟล์ข้อความ (แต่ละบรรทัดควรลงท้ายด้วย end-of-line)
  • ไฟล์ไบนารี (ไม่มี "เส้น" ที่แท้จริงที่จะพูดถึงและความยาวของไฟล์จะต้องเก็บรักษาไว้)

หากแต่ละบรรทัดลงท้ายด้วย end-of-line สิ่งนี้จะหลีกเลี่ยงตัวอย่างเช่นการต่อไฟล์ข้อความสองไฟล์เข้าด้วยกันจะทำให้บรรทัดสุดท้ายของบรรทัดแรกวิ่งเข้าไปในบรรทัดแรกของบรรทัดที่สอง

นอกจากนี้ตัวแก้ไขยังสามารถตรวจสอบการโหลดได้ว่าไฟล์จะลงท้ายด้วย end-of-line หรือไม่และบันทึกไว้ในตัวเลือกท้องถิ่น 'eol' และใช้ไฟล์นั้นเมื่อทำการเขียนไฟล์

ไม่กี่ปีหลัง (2005) บรรณาธิการจำนวนมาก (ZDE, Eclipse, Scite, ... ) ได้ "ลืม" EOL สุดท้ายซึ่งไม่ได้ชื่นชมมากนัก
ไม่เพียงแค่นั้น แต่พวกเขาตีความ EOL สุดท้ายว่าไม่ถูกต้องในฐานะ 'เริ่มต้นบรรทัดใหม่' และเริ่มแสดงอีกบรรทัดหนึ่งราวกับว่ามันมีอยู่แล้ว
สิ่งนี้สามารถมองเห็นได้ด้วยไฟล์ข้อความ 'ที่ถูกต้อง' พร้อมโปรแกรมแก้ไขข้อความที่มีความประพฤติดีเช่น vim เปรียบเทียบกับการเปิดในโปรแกรมแก้ไขด้านบน มันแสดงบรรทัดพิเศษใต้บรรทัดสุดท้ายของไฟล์ คุณเห็นอะไรเช่นนี้:

1 first line
2 middle line
3 last line
4

11
+1 ฉันพบคำถาม SO นี้แล้วในขณะที่ประสบปัญหานี้มาก มันน่ารำคาญมากที่ Eclipse จะแสดงบรรทัดสุดท้ายนี้ "ปลอม" และถ้าฉันลบมันออกไปแล้ว git (และเครื่องมือ unix อื่น ๆ ทั้งหมดที่คาดว่า EOL) จะบ่น นอกจากนี้โปรดทราบว่านี่ไม่เพียง แต่ในปี 2005: Eclipse 4.2 Juno ยังมีปัญหานี้อยู่
MestreLion

@MestreLion ความต่อเนื่องที่stackoverflow.com/questions/729692/…
Pacerier

46

เครื่องมือบางอย่างคาดหวังสิ่งนี้ ตัวอย่างเช่นwcคาดหวังสิ่งนี้:

$ echo -n "Line not ending in a new line" | wc -l
0
$ echo "Line ending with a new line" | wc -l
1

22
ฉันจะไม่พูดว่า "บางคน" ฉันพูดเครื่องมือส่วนใหญ่คาดหวังว่าสำหรับไฟล์ข้อความถ้าไม่ทั้งหมด แมว, คอมไพล์, diff, wc, grep, sed ... รายการมีขนาดใหญ่
MestreLion

บางทีอาจพูดwcได้ว่าไม่ได้คาดหวังสิ่งนี้มากเท่าที่มันทำงานได้ง่ายภายในนิยาม POSIX ของ "บรรทัด" ซึ่งตรงข้ามกับความเข้าใจที่เข้าใจง่ายของ "บรรทัด" ของคนส่วนใหญ่
Guildenstern

@Guildenstern ความหมายที่ใช้งานง่ายจะเป็นสำหรับwc -lการพิมพ์1ในทั้งสองกรณี 2แต่บางคนอาจจะบอกว่ากรณีที่สองควรพิมพ์
Flimm

@Flimm หากคุณคิดว่า\nเป็นตัวแบ่งบรรทัดแทนที่จะเป็นตัวคั่นบรรทัดดังที่ POSIX / UNIX ทำดังนั้นคาดว่ากรณีที่สองในการพิมพ์ 2 นั้นบ้าจริง ๆ
เซมิโคลอน

21

โดยทั่วไปมีหลายโปรแกรมที่จะไม่ประมวลผลไฟล์อย่างถูกต้องหากพวกเขาไม่ได้รับ EOL EOF ขั้นสุดท้าย

GCC เตือนคุณเกี่ยวกับสิ่งนี้เพราะคาดว่าเป็นส่วนหนึ่งของมาตรฐาน C (ส่วน 5.1.1.2 เห็นได้ชัด)

คำเตือนคอมไพเลอร์ "ไม่มีบรรทัดใหม่ที่ท้ายไฟล์"


5
GCC ไม่สามารถประมวลผลไฟล์ได้ แต่ต้องแจ้งเตือนเป็นส่วนหนึ่งของมาตรฐาน C
บิล Lizard

IIRC, MSVC 2005 บ่นเกี่ยวกับไฟล์ C ซึ่งจบลงด้วยบรรทัดที่ไม่สมบูรณ์และอาจปฏิเสธที่จะรวบรวม
Mark K Cowan

16

สิ่งนี้มาจากวันแรก ๆ เมื่อมีการใช้เทอร์มินัลอย่างง่าย อักขระขึ้นบรรทัดใหม่ถูกใช้เพื่อเรียก 'ล้างข้อมูลที่ถ่ายโอน

วันนี้อักขระขึ้นบรรทัดใหม่ไม่จำเป็นอีกต่อไป แน่นอนว่าแอพจำนวนมากยังคงมีปัญหาหากไม่มีการขึ้นบรรทัดใหม่ แต่ฉันคิดว่าจุดบกพร่องในแอพเหล่านั้น

หากคุณมีรูปแบบไฟล์ข้อความที่คุณต้องการให้ขึ้นบรรทัดใหม่คุณจะได้รับการตรวจสอบข้อมูลอย่างง่ายราคาถูกมาก: ถ้าไฟล์ลงท้ายด้วยบรรทัดที่ไม่มีบรรทัดใหม่ในตอนท้ายคุณจะรู้ว่าไฟล์เสีย มีเพียงหนึ่งไบต์พิเศษสำหรับแต่ละบรรทัดคุณสามารถตรวจพบไฟล์ที่เสียหายด้วยความแม่นยำสูงและแทบไม่มีเวลา CPU


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

14
พวกเราหลายคนไม่ใช้เครื่องมือ Unix เลยและเราก็ไม่สนใจ
DaveWalley

12
มันไม่ได้เป็นเพียงเครื่องมือยูนิกซ์เครื่องมือใด ๆ จะทำงานได้ดีขึ้นและ / หรือถูกเขียนโค้ดได้ง่ายขึ้นหากสามารถสันนิษฐานได้ว่ารูปแบบไฟล์ที่เหมาะสม
Sam Watkins

2
@Sam Watkins เห็นด้วยว่าการกำหนดรูปแบบที่เรียบง่ายเป็นสิ่งที่ดี ถึงกระนั้นรหัสก็ยังต้องมีความจริงและไม่ถือว่าข้อมูลนั้นเป็นไปตามรูปแบบ
chux - Reinstate Monica

8
@MestreLion นี่คือมรดกที่ไร้ประโยชน์จากชุดของเครื่องมือที่ไม่ดีตามมาตรฐานที่โง่ สิ่งประดิษฐ์เหล่านี้ของการเขียนโปรแกรมพวกหัวรุนแรง (เช่นไฟล์ทุกอย่าง! ทุกอย่างควรพูดข้อความล้วน) ไม่ได้ตายหลังจากการประดิษฐ์ของพวกเขาเพราะมันเป็นเครื่องมือเดียวที่มีในช่วงเวลาหนึ่ง C ถูกแทนที่โดย C ++ ไม่ใช่ส่วนหนึ่งของ POSIX ไม่ต้องมี EOL ที่ EOF และการใช้งานของมันลดลงอย่างชัดเจนจาก * nix luddists
polkovnikov.ph

14

กรณีการใช้งานแยกต่างหาก: เมื่อไฟล์ข้อความของคุณถูกควบคุมเวอร์ชัน (ในกรณีนี้โดยเฉพาะภายใต้คอมไพล์แม้ว่าจะใช้กับผู้อื่นด้วย) หากมีการเพิ่มเนื้อหาในตอนท้ายของไฟล์บรรทัดที่ก่อนหน้านี้บรรทัดสุดท้ายจะถูกแก้ไขเพื่อรวมอักขระบรรทัดใหม่ ซึ่งหมายความว่าการblameinging ไฟล์เพื่อค้นหาว่าเมื่อใดที่บรรทัดที่ถูกแก้ไขล่าสุดจะแสดงการเพิ่มข้อความไม่ใช่การคอมมิชชันก่อนหน้านั้นที่คุณต้องการดู


1
diff และตำหนิควรได้รับการอัปเดตเพื่อตรวจหา "บรรทัดใหม่" แทน "newlines" ( \n) แก้ไขปัญหา.
Andrew

1
คุณสามารถใช้แท็ก -w เพื่อเพิกเฉยต่อการเปลี่ยนแปลงของช่องว่าง แต่ไม่ใช่ค่าเริ่มต้น
Robin Whittleton

11

นอกเหนือจากเหตุผลในทางปฏิบัติข้างต้นแล้วมันจะไม่แปลกใจเลยถ้าผู้สร้าง Unix (Thompson, Ritchie, et al.) หรือผู้ทำ Multics รุ่นก่อนของพวกเขารู้ว่า คุณสามารถเข้ารหัสไฟล์ที่เป็นไปได้ทั้งหมด ด้วยตัวคั่นบรรทัดไม่มีความแตกต่างระหว่างไฟล์ของเส้นศูนย์และไฟล์ที่มีบรรทัดว่างหนึ่งบรรทัด ทั้งคู่ถูกเข้ารหัสเป็นไฟล์ที่มีอักขระศูนย์

ดังนั้นเหตุผลคือ:

  1. เพราะนั่นคือวิธีที่ POSIX กำหนดไว้
  2. เพราะเครื่องมือบางอย่างคาดหวังมันหรือ "ทำงานผิดปกติ" หากไม่มีมัน ตัวอย่างเช่นwc -lจะไม่นับ "บรรทัด" สุดท้ายหากไม่ได้ขึ้นบรรทัดใหม่
  3. เพราะมันง่ายและสะดวก บน Unix catทำงานได้ดีและทำงานได้โดยไม่มีความยุ่งยาก มันเพียงแค่คัดลอกไบต์ของแต่ละไฟล์โดยไม่จำเป็นต้องตีความใด ๆ ผมไม่คิดว่ามีเทียบเท่า DOS catเพื่อ การใช้copy a+b cจะสิ้นสุดการผสานบรรทัดสุดท้ายของไฟล์ที่มีบรรทัดแรกของไฟล์ab
  4. เนื่องจากไฟล์ (หรือสตรีม) ของเส้นศูนย์สามารถแยกความแตกต่างจากไฟล์ของหนึ่งบรรทัดว่าง

11

ฉันสงสัยตัวเองมาหลายปีแล้ว แต่วันนี้ฉันเจอเหตุผลที่ดี

ลองนึกภาพไฟล์ที่มีการบันทึกในทุกบรรทัด (เช่นไฟล์ CSV) และคอมพิวเตอร์กำลังเขียนบันทึกที่ท้ายไฟล์ แต่มันก็ล้มเหลวทันที Gee เป็นบรรทัดสุดท้ายที่สมบูรณ์หรือไม่ (ไม่ใช่สถานการณ์ที่ดี)

แต่ถ้าเรายุติบรรทัดสุดท้ายเสมอเราก็จะรู้ (เพียงตรวจสอบว่าบรรทัดสุดท้ายถูกยกเลิก) มิฉะนั้นเราอาจจะต้องทิ้งบรรทัดสุดท้ายทุกครั้งเพื่อความปลอดภัย


10

สมมุติว่ารหัสการแยกวิเคราะห์บางอย่างคาดว่าจะมี

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

แน่นอน - ถ้าคุณลงท้ายด้วยบรรทัดใหม่: มี (ในทางทฤษฎี) เป็นบรรทัดสุดท้ายที่ว่างเปล่าระหว่าง EOL และ EOF หรือไม่? หนึ่งในการไตร่ตรอง ...


12
มันไม่ได้เป็นกฎก็คือการประชุมกสายเป็นสิ่งที่ปลายกับสายการสิ้นสุดของ ดังนั้นไม่ไม่มี "บรรทัดสุดท้ายว่างเปล่า" ระหว่าง EOL และ EOF
MestreLion

4
@MestreLion: แต่ตัวละครที่เป็นปัญหานั้นไม่มีชื่อ "end-of-line" มันชื่อว่า "newline" และ / หรือ "linefeed" ตัวคั่นบรรทัดไม่ใช่ตัวคั่นบรรทัด และผลลัพธ์ก็คือบรรทัดว่างสุดท้าย
Ben Voigt

2
เครื่องมือไม่ (มีสติ) จะนับ EOL (CR, LF, ฯลฯ ) ล่าสุดของไฟล์เป็นบรรทัดว่างเพิ่มเติม และเครื่องมือ POSIX ทั้งหมดจะไม่นับอักขระสุดท้ายของไฟล์เป็นบรรทัดหากไม่มี EOL ที่สิ้นสุด โดยไม่คำนึงถึงตัวละคร EOL ชื่อเป็น "อาหารเส้น" หรือ "กลับรถ" (มีตัวละครที่ไม่มีชื่อ "ขึ้นบรรทัดใหม่") สำหรับ puposes ปฏิบัติเครื่องมือที่เหมาะสมรักษามันเป็นสายเทอร์มิไม่เป็นเส้นคั่น
MestreLion

2
@MestreLion คุณแน่ใจหรือว่า "line terminator" มีเหตุผลหรือไม่ คว้าโปรแกรมเมอร์ที่ไม่ใช่นักเขียนและทำการสำรวจอย่างรวดเร็ว คุณจะรู้ได้อย่างรวดเร็วว่าแนวคิดของเส้นใกล้กับแนวคิดของ "ตัวคั่นเส้น" แนวคิดของ "line terminator" นั้นแปลกมาก
Pacerier

4
@Sahuagin: นี่ไม่ใช่มุมมองของฉันนี่คือวิธีที่ POSIX Standard กำหนดบรรทัด ไฟล์ที่ว่างเปล่าด้วย 0 ไบต์มี 0 สายจึงไม่มี EOL และไฟล์ที่จะได้รับการพิจารณาว่ามีเพียงหนึ่งเดียวบรรทัดว่างก็ไม่จำเป็นต้องมีการ EOL โปรดทราบว่าสิ่งนี้มีความเกี่ยวข้องหากคุณต้องการนับบรรทัดในไฟล์เนื่องจากเครื่องมือแก้ไขใด ๆ จะอนุญาตให้คุณ "รับ" ไปยังบรรทัดถัดไป (หรือบรรทัดแรก) ไม่ว่าจะมี EOL อยู่หรือไม่
MestreLion

10

นอกจากนี้ยังมีปัญหาการเขียนโปรแกรมในทางปฏิบัติด้วยไฟล์ที่ไม่มีบรรทัดใหม่ในตอนท้าย: readBash ในตัว (ฉันไม่รู้เกี่ยวกับreadการใช้งานอื่น ๆ) ไม่ทำงานตามที่คาดไว้:

printf $'foo\nbar' | while read line
do
    echo $line
done

พิมพ์นี้เท่านั้นfoo ! เหตุผลก็คือเมื่อreadพบบรรทัดสุดท้ายมันจะเขียนเนื้อหาไป$lineแต่จะส่งกลับรหัสทางออก 1 เนื่องจากถึง EOF นี่เป็นการแบ่งwhileลูปดังนั้นเราจึงไม่เคยไปถึงecho $lineส่วนนี้ หากคุณต้องการจัดการกับสถานการณ์นี้คุณต้องทำสิ่งต่อไปนี้:

while read line || [ -n "${line-}" ]
do
    echo $line
done < <(printf $'foo\nbar')

กล่าวคือทำechoถ้าreadล้มเหลวเนื่องจากบรรทัดไม่ว่างท้ายไฟล์ โดยธรรมชาติในกรณีนี้จะมีการขึ้นบรรทัดใหม่หนึ่งบรรทัดในเอาต์พุตที่ไม่ได้อยู่ในอินพุต


9

ทำไมไฟล์ (ข้อความ) ถึงลงท้ายด้วยการขึ้นบรรทัดใหม่

หลายคนแสดงออกเช่นกันเพราะ:

  1. หลายโปรแกรมทำงานได้ไม่ดีหรือล้มเหลวหากไม่มี

  2. แม้โปรแกรมที่จัดการกับไฟล์จะไม่มีจุดสิ้นสุด'\n'ฟังก์ชั่นของเครื่องมืออาจไม่เป็นไปตามความคาดหวังของผู้ใช้ซึ่งอาจไม่ชัดเจนในมุมนี้

  3. โปรแกรมไม่อนุญาตขั้นสุดท้าย'\n'(ฉันไม่ทราบเลย)


แต่นี่เป็นคำถามต่อไป:

รหัสควรทำอย่างไรกับไฟล์ข้อความที่ไม่มีบรรทัดใหม่

  1. ที่สำคัญที่สุด - ไม่ได้เขียนรหัสที่ถือว่าเป็นไฟล์ข้อความปลายมีขึ้นบรรทัดใหม่ การสันนิษฐานว่าไฟล์เป็นไปตามรูปแบบที่นำไปสู่ความเสียหายของข้อมูลการโจมตีของแฮ็กเกอร์และการล่ม ตัวอย่าง:

    // Bad code
    while (fgets(buf, sizeof buf, instream)) {
      // What happens if there is no \n, buf[] is truncated leading to who knows what
      buf[strlen(buf) - 1] = '\0';  // attempt to rid trailing \n
      ...
    }
    
  2. หากต้องการการติดตามครั้งสุดท้าย'\n'แจ้งเตือนผู้ใช้ถึงการขาดงานและการดำเนินการ IOW ตรวจสอบความถูกต้องของรูปแบบของไฟล์ หมายเหตุ: สิ่งนี้อาจรวมถึงความยาวบรรทัดสูงสุดการเข้ารหัสอักขระ ฯลฯ

  3. '\n'กำหนดอย่างชัดเจนเอกสารการจัดการรหัสของที่หายไปสุดท้าย

  4. ไม่ได้เป็นไปได้สร้าง'\n'ไฟล์ขาดเป็นตอนจบ


4

มันช้ามากที่นี่ แต่ฉันเพิ่งพบข้อผิดพลาดหนึ่งในการประมวลผลไฟล์และที่มาเพราะไฟล์ไม่ได้ลงท้ายด้วยบรรทัดใหม่ที่ว่างเปล่า เรากำลังประมวลผลไฟล์ข้อความด้วยsedและsedละเว้นบรรทัดสุดท้ายจากเอาต์พุตซึ่งทำให้โครงสร้าง json ไม่ถูกต้องและการส่งส่วนที่เหลือของกระบวนการล้มเหลว

สิ่งที่เราทำคือ:

มีไฟล์ตัวอย่างหนึ่งไฟล์ที่บอกว่า: foo.txtมีjsonเนื้อหาอยู่ข้างใน

[{
    someProp: value
},
{
    someProp: value
}] <-- No newline here

ไฟล์ถูกสร้างขึ้นในเครื่องแม่ม่ายและสคริปต์หน้าต่างกำลังประมวลผลไฟล์นั้นโดยใช้คำสั่ง PowerShell ทั้งหมดดี.

เมื่อเราประมวลผลไฟล์เดียวกันโดยใช้sedคำสั่งsed 's|value|newValue|g' foo.txt > foo.txt.tmp

ไฟล์ที่สร้างขึ้นใหม่คือ

[{
    someProp: value
},
{
    someProp: value

และบูมมันล้มเหลวในกระบวนการที่เหลือเพราะ JSON ไม่ถูกต้อง

ดังนั้นจึงเป็นการดีที่จะจบไฟล์ของคุณด้วยบรรทัดใหม่ที่ว่างเปล่า


3

ฉันอยู่ภายใต้การแสดงผลเสมอกฎมาจากวันที่แยกไฟล์โดยไม่ต้องขึ้นบรรทัดใหม่เป็นเรื่องยาก นั่นคือคุณจะสิ้นสุดการเขียนโค้ดที่กำหนดจุดสิ้นสุดของบรรทัดโดยอักขระ EOL หรือ EOF มันง่ายกว่าที่จะสมมติว่าบรรทัดลงท้ายด้วย EOL

อย่างไรก็ตามฉันเชื่อว่ากฎนั้นมาจากคอมไพเลอร์ C ที่ต้องการขึ้นบรรทัดใหม่ และดังที่ระบุไว้ในคำเตือน“ ไม่มีการขึ้นบรรทัดใหม่เมื่อสิ้นสุดไฟล์” คอมไพเลอร์ #include จะไม่เพิ่มบรรทัดใหม่


0

ลองนึกภาพว่าไฟล์นั้นกำลังถูกประมวลผลในขณะที่ไฟล์นั้นยังคงถูกสร้างขึ้นโดยกระบวนการอื่น

มันอาจจะเกี่ยวข้องกับเรื่องนั้นเหรอ? แฟล็กที่ระบุว่าไฟล์พร้อมที่จะประมวลผล


-4

ฉันชอบเส้นใหม่ที่ส่วนท้ายของไฟล์รหัสต้นฉบับ

อาจมีต้นกำเนิดมาจาก Linux หรือระบบ UNIX ทั้งหมดสำหรับเรื่องนั้น ฉันจำได้ว่ามีข้อผิดพลาดในการรวบรวม (gcc หากฉันไม่ผิดพลาด) เพราะไฟล์ซอร์สโค้ดไม่ได้จบด้วยบรรทัดใหม่ที่ว่างเปล่า เหตุใดจึงทำให้วิธีนี้เป็นสิ่งที่น่าสงสัย


-6

IMHO มันเป็นเรื่องของสไตล์และความคิดเห็นส่วนตัว

ในสมัยก่อนฉันไม่ได้ขึ้นบรรทัดใหม่ อักขระที่บันทึกหมายถึงความเร็วที่มากขึ้นผ่านโมเด็ม 14.4K นั้น

ต่อมาฉันวางบรรทัดใหม่เพื่อให้ง่ายขึ้นในการเลือกบรรทัดสุดท้ายโดยใช้ shift + downarrow

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.