Tại sao các tệp văn bản nên kết thúc bằng một dòng mới?


1469

Tôi giả sử mọi người ở đây đều quen thuộc với câu ngạn ngữ rằng tất cả các tệp văn bản nên kết thúc bằng một dòng mới. Tôi đã biết đến "quy tắc" này trong nhiều năm nhưng tôi luôn tự hỏi - tại sao?


30
chỉ là một nitlog. nó không phải là "dòng mới" ở cuối tập tin. Đó là một "ngắt dòng" ở cuối dòng cuối cùng. Ngoài ra, hãy xem câu trả lời hay nhất cho một câu hỏi liên quan: stackoverflow.com/questions/16222530/iêu
gcb

346
Chỉ cần viết thêm một chút nữa, anh ta đã không thực sự viết dòng mới, anh ấy đã viết ra dòng sản phẩm mới, đó là chính xác.
sindrenm

5
không quen thuộc, nhưng tôi tự hỏi tôi thực sự bởi vì số lượng các trường hợp mà dòng mới thừa thãi đó thực sự phá vỡ mọi thứ là quá cao so với thị hiếu của tôi
tobibeer

2
Tôi hiện đang sử dụng các luồng Node.js để phân tích từng dòng dữ liệu văn bản đơn giản và việc thiếu ngắt dòng thiết bị đầu cuối rất khó chịu, vì tôi phải thêm logic bổ sung khi phía đầu vào của luồng kết thúc / đóng để đảm bảo rằng dòng cuối cùng được xử lý.
Mark K Cowan

23
Các cách Unix coi hành vi chung của nó ở phần cuối của file như sau: \ n ký tự không bắt đầu dòng; thay vào đó, họ kết thúc chúng. Vì vậy, \ n là dấu kết thúc dòng, không phải là dấu phân cách dòng. Dòng đầu tiên (giống như tất cả các dòng) không cần \ n để bắt đầu nó. Dòng cuối cùng (giống như tất cả các dòng) cần \ n để kết thúc nó. \ N ở cuối tệp không tạo thêm một dòng. Tuy nhiên, đôi khi, các trình soạn thảo văn bản sẽ thêm một dòng trống có thể nhìn thấy ở đó. Ngay cả emacs cũng làm như vậy, tùy ý .
MarkDBlackwell

Câu trả lời:


1382

Bởi vì đó là cách tiêu chuẩn POSIX định nghĩa một dòng :

Đường 3.206
Một chuỗi gồm 0 hoặc nhiều ký tự không phải <dòng mới> cộng với ký tự <dòng mới> kết thúc.

Do đó, các dòng không kết thúc bằng một ký tự dòng mới không được coi là các dòng thực tế. Đó là lý do tại sao một số chương trình gặp sự cố khi xử lý dòng cuối cùng của tệp nếu dòng đó không bị chấm dứt.

Có ít nhất một lợi thế cho hướng dẫn này khi làm việc trên trình giả lập thiết bị đầu cuối: Tất cả các công cụ Unix đều mong đợi quy ước này và hoạt động với nó. Chẳng hạn, khi nối các tệp với cat, một tệp bị chấm dứt bởi dòng mới sẽ có hiệu ứng khác với tệp không có:

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

Và, như ví dụ trước cũng chứng minh, khi hiển thị tệp trên dòng lệnh (ví dụ: thông qua more), tệp kết thúc dòng mới sẽ hiển thị chính xác. Một tập tin kết thúc không đúng có thể bị cắt xén (dòng thứ hai).

Để thống nhất, rất hữu ích khi tuân theo quy tắc này - làm khác đi sẽ phát sinh thêm công việc khi xử lý các công cụ Unix mặc định.


Hãy suy nghĩ về nó một cách khác biệt: Nếu các dòng không bị chấm dứt bởi dòng mới, việc tạo các lệnh như cathữu ích sẽ khó hơn nhiều: làm thế nào để bạn thực hiện một lệnh để ghép các tệp sao cho

  1. nó đặt mỗi tệp bắt đầu trên một dòng mới, đó là những gì bạn muốn 95% thời gian; nhưng
  2. nó cho phép hợp nhất dòng cuối cùng và đầu tiên của hai tệp, như trong ví dụ trên giữa b.txtc.txt?

Tất nhiên điều này có thể giải quyết được nhưng bạn cần sử dụng catphức tạp hơn (bằng cách thêm các đối số dòng lệnh vị trí, ví dụ cat a.txt --no-newline b.txt c.txt), và bây giờ lệnh thay vì mỗi tệp riêng lẻ kiểm soát cách dán cùng với các tệp khác. Điều này gần như chắc chắn không thuận tiện.

Bạn có thể cần phải giới thiệu một ký tự đặc biệt để đánh dấu một dòng được cho là tiếp tục thay vì chấm dứt. Chà, bây giờ bạn đang bị mắc kẹt với tình huống tương tự như trên POSIX, ngoại trừ đảo ngược (tiếp tục dòng thay vì ký tự kết thúc dòng).


Bây giờ, trên các hệ thống không tuân thủ POSIX (ngày nay hầu hết là Windows), vấn đề chính là: các tệp thường không kết thúc bằng một dòng mới và định nghĩa (không chính thức) của một dòng có thể là văn bản được phân tách bởi dòng mới. (lưu ý nhấn mạnh). Điều này là hoàn toàn hợp lệ. Tuy nhiên, đối với dữ liệu có cấu trúc (ví dụ mã lập trình), nó làm cho việc phân tích cú pháp phức tạp hơn một chút: nó thường có nghĩa là các trình phân tích cú pháp phải được viết lại. Nếu một trình phân tích cú pháp ban đầu được viết với định nghĩa POSIX, thì việc sửa đổi luồng mã thông báo sẽ dễ dàng hơn so với trình phân tích cú pháp - nói cách khác, hãy thêm mã thông báo nhân tạo mới vào dòng đầu vào một đầu vào.


9
Mặc dù bây giờ khá không thực tế để khắc phục, rõ ràng POSIX đã mắc lỗi khi xác định dòng - làm bằng chứng cho số lượng câu hỏi liên quan đến vấn đề này. Một dòng nên được xác định là 0 hoặc nhiều ký tự được kết thúc bởi <eol>, <eof> hoặc <eol> <eof>. Sự phức tạp của trình phân tích cú pháp không phải là một mối quan tâm hợp lệ. Sự phức tạp, bất cứ nơi nào có thể, nên được chuyển từ đầu lập trình viên và vào thư viện.
Doug Coburn

23
@DougCoburn Câu trả lời này từng có một cuộc thảo luận kỹ thuật đầy đủ, giải thích tại sao điều này là sai và tại sao POSIX lại làm đúng. Thật không may, những bình luận này gần đây đã bị xóa bởi một người điều hành quá nhiệt tình. Tóm lại, nó không phải là về phân tích phức tạp; thay vào đó, định nghĩa của bạn làm cho các công cụ tác giả khó hơn nhiều, cattheo cách vừa hữu ích vừa nhất quán.
Konrad Rudolph

8
@Leon Quy tắc POSIX là tất cả về việc giảm các trường hợp cạnh. Và nó làm rất đẹp. Tôi thực sự cảm thấy hụt hẫng khi mọi người không hiểu điều này: Đó là định nghĩa đơn giản nhất, có thể tự nhất quán của một dòng.
Konrad Rudolph

6
@BT Tôi nghĩ rằng bạn cho rằng ví dụ của tôi về quy trình làm việc thuận tiện hơn là lý do đằng sau quyết định. Không phải, đó chỉ là hậu quả. Các lý do là các quy tắc POSIX là nguyên tắc đó là đơn giản nhất, và khiến cho dòng xử lý trong một phân tích cú pháp dễ nhất. Lý do duy nhất chúng ta thậm chí có cuộc tranh luận là Windows làm điều đó khác đi, và do đó, có rất nhiều công cụ thất bại trên các tệp POSIX. Nếu mọi người đã làm POSIX, sẽ không có vấn đề gì. Tuy nhiên, mọi người phàn nàn về POSIX, không phải về Windows.
Konrad Rudolph

7
@BT Tôi chỉ đề cập đến Windows để chỉ ra các trường hợp quy tắc POSIX không có ý nghĩa (nói cách khác, tôi đã ném cho bạn một cục xương). Tôi hạnh phúc hơn bao giờ hết khi nhắc đến nó trong cuộc thảo luận này. Nhưng sau đó, yêu cầu của bạn thậm chí còn ít ý nghĩa hơn: trên các nền tảng POSIX, thật đơn giản để thảo luận về các tệp văn bản với các quy ước kết thúc dòng khác nhau, bởi vì không có lý do gì để tạo ra chúng. Lợi thế là gì? Có nghĩa đen là không có. - Tóm lại, tôi thực sự không hiểu sự thù hận mà câu trả lời này (hoặc quy tắc POSIX) đang gây ra. Thành thật mà nói, nó hoàn toàn phi lý.
Konrad Rudolph

282

Mỗi dòng nên được chấm dứt trong một ký tự dòng mới, bao gồm cả dòng cuối cùng. Một số chương trình có vấn đề khi xử lý dòng cuối cùng của tệp nếu không phải là dòng mới bị chấm dứt.

GCC cảnh báo về điều đó không phải vì nó không thể xử lý tệp mà vì nó phải là một phần của tiêu chuẩn.

Tiêu chuẩn ngôn ngữ C cho biết Một tệp nguồn không trống sẽ kết thúc bằng một ký tự dòng mới, không được đặt ngay trước ký tự dấu gạch chéo ngược.

Vì đây là mệnh đề "sẽ", chúng tôi phải phát ra một thông báo chẩn đoán vi phạm quy tắc này.

Đây là trong mục 2.1.1.2 của tiêu chuẩn ANSI C 1989. Mục 5.1.1.2 của tiêu chuẩn ISO C 1999 (và có lẽ cũng là tiêu chuẩn ISO C 1990).

Tham khảo: Kho lưu trữ thư GCC / GNU .


17
vui lòng viết các chương trình tốt sau đó cho phép chèn dòng mới đó vào nơi cần thiết trong khi xử lý hoặc có thể xử lý chính xác các "thiếu" ... thực tế là không bị thiếu
tobibeer 13/215

4
@BilltheLizard, một số ví dụ về "Một số chương trình có vấn đề khi xử lý dòng cuối cùng của tệp nếu không phải là dòng mới bị chấm dứt" ?
Pacerier

4
@Pacerier wc -lsẽ không đếm dòng cuối cùng của tệp nếu nó không phải là dòng mới bị chấm dứt. Ngoài ra, catsẽ nối dòng cuối cùng của tệp với dòng đầu tiên của tệp tiếp theo thành một nếu dòng cuối cùng của tệp đầu tiên không phải là dòng mới kết thúc. Hầu như bất kỳ chương trình nào đang tìm kiếm các dòng mới như một dấu phân cách đều có khả năng làm hỏng điều này.
Bill Lizard

2
@BilltheLizard, tôi có nghĩa là wcđã được đề cập ....
Pacerier

2
@BilltheLizard, My bad, để làm rõ: một số ví dụ về các chương trình có vấn đề xử lý dòng cuối cùng của tệp nếu nó không phải là dòng mới bị chấm dứt (bên cạnh những chương trình đã được đề cập hàng loạt trên luồng như ) catwc?
Pacerier

116

Câu trả lời này là một nỗ lực tại một câu trả lời kỹ thuật hơn là ý kiến.

Nếu chúng tôi muốn trở thành người theo chủ nghĩa POSIX, chúng tôi xác định một dòng là:

Một chuỗi gồm 0 hoặc nhiều ký tự không phải <dòng mới> cộng với ký tự <dòng mới> kết thúc.

Nguồn: https://pub.opengroup.org/onlinepub/9699919799/basingefs/V1_chap03.html#tag_03_206

Một dòng không đầy đủ như:

Một chuỗi gồm một hoặc nhiều ký tự không phải <dòng mới> ở cuối tệp.

Nguồn: https://pub.opengroup.org/onlinepub/9699919799/basingefs/V1_chap03.html#tag_03_195

Một tệp văn bản như:

Một tệp chứa các ký tự được tổ chức thành không hoặc nhiều dòng. Các dòng không chứa các ký tự NUL và không có ký tự nào có thể vượt quá độ dài {LINE_MAX}, bao gồm cả ký tự <newline>. Mặc dù POSIX.1-2008 không phân biệt giữa tệp văn bản và tệp nhị phân (xem tiêu chuẩn ISO C), nhiều tiện ích chỉ tạo ra đầu ra có thể dự đoán hoặc có ý nghĩa khi hoạt động trên tệp văn bản. Các tiện ích tiêu chuẩn có các hạn chế như vậy luôn chỉ định "tệp văn bản" trong phần STDIN hoặc INPUT PHIM.

Nguồn: https://pub.opengroup.org/onlinepub/9699919799/basingefs/V1_chap03.html#tag_03_397

Một chuỗi như:

Một chuỗi các byte liền kề được kết thúc bởi và bao gồm byte null đầu tiên.

Nguồn: https://pub.opengroup.org/onlinepub/9699919799/basingefs/V1_chap03.html#tag_03_394

Từ đó, chúng ta có thể rút ra rằng lần duy nhất chúng ta có khả năng gặp phải bất kỳ loại sự cố nào là nếu chúng ta xử lý khái niệm về một dòng của tệp hoặc tệp dưới dạng tệp văn bản (vì tệp văn bản là một tổ chức bằng không hoặc nhiều dòng hơn và một dòng chúng tôi biết phải chấm dứt bằng <dòng mới>).

Trường hợp tại điểm : wc -l filename.

Từ wchướng dẫn của chúng tôi, chúng tôi đọc:

Một dòng được định nghĩa là một chuỗi các ký tự được phân tách bằng ký tự <newline>.

Ý nghĩa của các tệp JavaScript, HTML và CSS là chúng là các tệp văn bản là gì?

Trong các trình duyệt, IDE hiện đại và các ứng dụng ngoại vi khác, không có vấn đề gì khi bỏ qua EOL tại EOF. Các ứng dụng sẽ phân tích các tập tin đúng cách. Do đó, không phải tất cả các Hệ điều hành đều tuân thủ tiêu chuẩn POSIX, do đó, sẽ không thực tế đối với các công cụ không phải hệ điều hành (ví dụ: trình duyệt) để xử lý các tệp theo tiêu chuẩn POSIX (hoặc bất kỳ tiêu chuẩn cấp hệ điều hành nào).

Do đó, chúng ta có thể tương đối tin tưởng rằng EOL ở EOF sẽ hầu như không có tác động tiêu cực ở cấp ứng dụng - bất kể nó có chạy trên HĐH UNIX hay không.

Tại thời điểm này, chúng tôi có thể tự tin nói rằng bỏ qua EOL tại EOF là an toàn khi giao dịch với JS, HTML, CSS ở phía máy khách. Trên thực tế, chúng tôi có thể tuyên bố rằng việc thu nhỏ bất kỳ một trong các tệp này, không chứa <newline> là an toàn.

Chúng ta có thể tiến thêm một bước này và nói rằng theo như NodeJS có liên quan thì nó cũng không thể tuân thủ tiêu chuẩn POSIX vì nó có thể chạy trong môi trường không tuân thủ POSIX.

Chúng ta còn lại gì sau đó? Công cụ cấp hệ thống.

Điều này có nghĩa là các vấn đề duy nhất có thể phát sinh là với các công cụ nỗ lực tuân thủ chức năng của chúng theo ngữ nghĩa của POSIX (ví dụ: định nghĩa của một dòng như trong hình wc).

Mặc dù vậy, không phải tất cả các shell sẽ tự động tuân thủ POSIX. Bash chẳng hạn không mặc định cho hành vi POSIX. Có một công tắc để kích hoạt nó : POSIXLY_CORRECT.

Thực phẩm cho suy nghĩ về giá trị của EOL là <newline>: https://www.rfc-editor.org/old/EOLstory.txt

Tiếp tục theo dõi công cụ, cho tất cả các ý định và mục đích thực tế, hãy xem xét điều này:

Hãy làm việc với một tệp không có EOL. Khi viết, tệp trong ví dụ này là một JavaScript được rút gọn không có 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

Lưu ý catkích thước tập tin chính xác là tổng của các phần riêng lẻ của nó. Nếu việc ghép các tệp JavaScript là mối quan tâm đối với các tệp JS, thì mối quan tâm thích hợp hơn sẽ là bắt đầu mỗi tệp JavaScript bằng dấu chấm phẩy.

Như một người khác đã đề cập trong chủ đề này: nếu bạn muốn cathai tệp có đầu ra chỉ là một dòng thay vì hai thì sao? Nói cách khác, catlàm những gì nó phải làm.

Các mancác catchỉ đề cập đến việc đọc đầu vào lên đến EOF, không <xuống dòng>. Lưu ý rằng công -ntắc của catcũng sẽ in ra một dòng kết thúc không phải là <newline> (hoặc dòng không đầy đủ ) dưới dạng một dòng - vì số đếm bắt đầu từ 1 (theo man.)

-n Đánh số các dòng đầu ra, bắt đầu từ 1.

Bây giờ chúng tôi hiểu cách POSIX định nghĩa một dòng , hành vi này trở nên mơ hồ hoặc thực sự không tuân thủ.

Hiểu mục đích và sự tuân thủ của một công cụ nhất định sẽ giúp xác định mức độ quan trọng của việc kết thúc các tệp bằng EOL. Trong C, C ++, Java (JAR), v.v ... một số tiêu chuẩn sẽ đưa ra một dòng mới về tính hợp lệ - không có tiêu chuẩn nào như vậy tồn tại đối với JS, HTML, CSS.

Ví dụ: thay vì sử dụng wc -l filenamemột cách có thể làm awk '{x++}END{ print x}' filenamevà hãy yên tâm rằng thành công của nhiệm vụ không bị nguy hiểm bởi một tệp mà chúng tôi có thể muốn xử lý mà chúng tôi đã không viết (ví dụ: thư viện bên thứ ba như JS đã rút gọn chúng tôi curld) - trừ khi chúng tôi ý định thực sự là đếm các dòng theo nghĩa tuân thủ POSIX.

Phần kết luận

Sẽ có rất ít trường hợp sử dụng thực tế trong đó bỏ qua EOL tại EOF đối với các tệp văn bản nhất định như JS, HTML và CSS sẽ có tác động tiêu cực - nếu có. Nếu chúng tôi dựa vào <newline>, chúng tôi sẽ hạn chế độ tin cậy của công cụ của chúng tôi đối với các tệp mà chúng tôi tạo ra và tự mở ra các lỗi tiềm ẩn do các tệp của bên thứ ba giới thiệu.

Đạo đức của câu chuyện: Kỹ sư công cụ không có điểm yếu là dựa vào EOL tại EOF.

Vui lòng gửi các trường hợp sử dụng khi chúng áp dụng cho JS, HTML và CSS nơi chúng tôi có thể kiểm tra việc bỏ qua EOL có ảnh hưởng xấu như thế nào.


2
POSIX không được gắn thẻ trong câu hỏi ... wat về kết thúc dòng MVS / OS? hoặc kết thúc dòng MS-DOS? Nhân tiện, tất cả các hệ thống posix đã biết đều cho phép các tệp văn bản không có dòng kết thúc cuối cùng (không tìm thấy trường hợp nào của hệ thống xác nhận tuân thủ posix mà "tệp văn bản" có xử lý đặc biệt trong kernel để chèn một dòng mới phù hợp trong trường hợp không có dòng mới nó)
Luis Colorado

62

Nó có thể liên quan đến sự khác biệt giữa :

  • tệp văn bản (mỗi dòng được cho là kết thúc ở cuối dòng)
  • tệp nhị phân (không có "dòng" thực sự để nói và độ dài của tệp phải được giữ nguyên)

Nếu mỗi dòng kết thúc ở một dòng cuối, thì điều này sẽ tránh, ví dụ, việc nối hai tệp văn bản sẽ làm cho dòng cuối cùng của dòng đầu tiên chạy vào dòng đầu tiên của dòng thứ hai.

Ngoài ra, một biên tập viên có thể kiểm tra tải xem tệp có kết thúc ở cuối dòng hay không, lưu nó trong tùy chọn cục bộ 'eol' và sử dụng tệp đó khi ghi tệp.

Vài năm trước (2005), nhiều biên tập viên (ZDE, Eclipse, Scite, ...) đã "quên" EOL cuối cùng, vốn không được đánh giá cao .
Không chỉ vậy, nhưng họ giải thích rằng EOL cuối cùng không chính xác, như 'bắt đầu một dòng mới', và thực sự bắt đầu hiển thị một dòng khác như thể nó đã tồn tại.
Điều này rất dễ thấy với tệp văn bản 'phù hợp' với trình soạn thảo văn bản hoạt động tốt như vim, so với mở tệp trong một trong các trình soạn thảo ở trên. Nó hiển thị một dòng phụ bên dưới dòng cuối cùng thực sự của tập tin. Bạn thấy một cái gì đó như thế này:

1 first line
2 middle line
3 last line
4

11
+1. Tôi đã tìm thấy câu hỏi SO này trong khi gặp vấn đề này. Nó là rất khó chịu của Eclipse để hiển thị này "giả" dòng cuối cùng, và Nếu tôi loại bỏ nó, sau đó git (và tất cả các công cụ unix khác mà hy vọng EOL) than phiền. Ngoài ra, lưu ý rằng điều này không chỉ trong năm 2005: Eclipse 4.2 Juno vẫn có vấn đề này.
MestreLion

@MestreLion, Tiếp tục tại stackoverflow.com/questions/729692/ khăn
Pacerier

46

Một số công cụ mong đợi điều này. Ví dụ, wcmong đợi điều này:

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

22
Tôi sẽ không nói "một số", tôi nói hầu hết các công cụ mong đợi rằng đối với các tệp văn bản, nếu không phải là tất cả. cat, git, diff, wc, grep, sed ... danh sách rất lớn
MestreLion

Có lẽ người ta có thể nói rằng wckhông mong đợi điều này, cũng như nó chỉ hoạt động theo định nghĩa POSIX của một "dòng" trái ngược với sự hiểu biết trực quan của hầu hết mọi người về "dòng".
Guildenstern

@Guildenstern Định nghĩa trực quan sẽ được wc -lin 1trong cả hai trường hợp, nhưng một số người có thể nói trường hợp thứ hai nên in 2.
Flimm

@Flimm Nếu bạn nghĩ về việc \nkết thúc dòng, thay vì phân tách dòng, như POSIX / UNIX, thì mong đợi trường hợp thứ hai để in 2 là hoàn toàn điên rồ.
dấu chấm phẩy

21

Về cơ bản, có nhiều chương trình sẽ không xử lý tệp chính xác nếu chúng không nhận được EOL EOF cuối cùng.

GCC cảnh báo bạn về điều này bởi vì nó được dự kiến ​​là một phần của tiêu chuẩn C. (phần 5.1.1.2 rõ ràng)

Cảnh báo trình biên dịch "Không có dòng mới ở cuối tập tin"


5
GCC không có khả năng xử lý tệp, nó phải đưa ra cảnh báo như là một phần của tiêu chuẩn C.
Bill the Lizard

IIRC, MSVC 2005 đã phàn nàn về các tệp C kết thúc bằng các dòng không đầy đủ và có thể từ chối biên dịch chúng.
Mark K Cowan

16

Điều này bắt nguồn từ những ngày đầu khi các thiết bị đầu cuối đơn giản được sử dụng. Char dòng mới được sử dụng để kích hoạt 'tuôn ra' dữ liệu được truyền.

Ngày nay, char dòng mới không cần thiết nữa. Chắc chắn, nhiều ứng dụng vẫn có vấn đề nếu dòng mới không có, nhưng tôi cho rằng đó là một lỗi trong các ứng dụng đó.

Tuy nhiên, nếu bạn có định dạng tệp văn bản nơi bạn yêu cầu dòng mới, bạn sẽ nhận được xác minh dữ liệu đơn giản rất rẻ: nếu tệp kết thúc bằng một dòng không có dòng mới ở cuối, bạn biết rằng tệp bị hỏng. Chỉ với một byte bổ sung cho mỗi dòng, bạn có thể phát hiện các tệp bị hỏng với độ chính xác cao và gần như không có thời gian CPU.


15
ngày nay, dòng mới tại EOF cho các tệp văn bản có thể không phải là một yêu cầu, nhưng nó là một quy ước hữu ích làm cho hầu hết các công cụ unix hoạt động cùng với kết quả nhất quán. Nó không phải là một lỗi ở tất cả.
MestreLion

14
Rất nhiều người trong chúng ta không sử dụng các công cụ Unix và chúng tôi không quan tâm.
DaveWalley

12
Không chỉ là các công cụ unix, bất kỳ công cụ nào cũng sẽ hoạt động tốt hơn và / hoặc được mã hóa đơn giản hơn nếu nó có thể giả định các định dạng tệp hợp lý.
Sam Watkins

2
@Sam Watkins Đồng ý có các định dạng đơn giản cũng được xác định là tốt. Tuy nhiên, mã vẫn cần phải xác thực và không giả sử, dữ liệu tuân thủ định dạng.
chux - Phục hồi Monica

8
@MestreLion Đây là một di sản vô dụng từ một bộ công cụ xấu tuân thủ các tiêu chuẩn ngu ngốc. Những tạo tác của lập trình cực đoan (tức là tệp của mọi thứ! Mọi thứ nên nói về văn bản đơn giản!) Đã không chết ngay sau phát minh của chúng vì chúng là công cụ duy nhất có sẵn tại một thời điểm nhất định của lịch sử. C đã được thay thế bởi C ++, nó không phải là một phần của POSIX, nó không yêu cầu EOL tại EOF và việc sử dụng nó (rõ ràng) không được khuyến khích bởi các luddists * nix.
polkovnikov.ph

14

Một trường hợp sử dụng riêng: khi tệp văn bản của bạn được kiểm soát phiên bản (trong trường hợp này cụ thể theo git mặc dù nó cũng áp dụng cho những người khác). Nếu nội dung được thêm vào cuối tệp, thì dòng trước đó là dòng cuối cùng sẽ được chỉnh sửa để bao gồm một ký tự dòng mới. Điều này có nghĩa là blameing tệp để tìm ra khi dòng đó được chỉnh sửa lần cuối sẽ hiển thị bổ sung văn bản, không phải là cam kết trước đó mà bạn thực sự muốn xem.


1
khác biệt và đổ lỗi chỉ nên được cập nhật để phát hiện "dòng mới" chứ không phải "dòng mới" ( \n). Vấn đề được giải quyết.
Andrew

1
Bạn có thể sử dụng thẻ -w để bỏ qua các thay đổi khoảng trắng, nhưng chúng không phải là mặc định.
Robin Whittleton

11

Ngoài những lý do thực tế trên, tôi sẽ không ngạc nhiên nếu những người khởi tạo Unix (Thompson, Ritchie và cộng sự) hoặc những người tiền nhiệm Multics của họ nhận ra rằng có một lý do lý thuyết để sử dụng các đầu cuối dòng thay vì phân tách dòng: Với dòng terminator, bạn có thể mã hóa tất cả các tập tin có thể có của dòng. Với các dấu phân cách dòng, không có sự khác biệt giữa một tệp có các dòng 0 và một tệp chứa một dòng trống duy nhất; cả hai đều được mã hóa dưới dạng tệp chứa ký tự không.

Vì vậy, lý do là:

  1. Bởi vì đó là cách POSIX định nghĩa nó.
  2. Bởi vì một số công cụ mong đợi nó hoặc "hoạt động sai" mà không có nó. Ví dụ: wc -lsẽ không tính một "dòng" cuối cùng nếu nó không kết thúc bằng một dòng mới.
  3. Bởi vì nó đơn giản và tiện lợi. Trên Unix, catchỉ hoạt động và nó hoạt động mà không có biến chứng. Nó chỉ sao chép các byte của mỗi tệp mà không cần phải giải thích. Tôi không nghĩ có một DOS tương đương cat. Sử dụng copy a+b csẽ kết thúc hợp nhất dòng cuối cùng của tệp avới dòng đầu tiên của tệp b.
  4. Bởi vì một tệp (hoặc luồng) của các dòng 0 có thể được phân biệt với một tệp của một dòng trống.

11

Tôi đã tự hỏi điều này trong nhiều năm. Nhưng tôi đã đi qua một lý do tốt ngày hôm nay.

Hãy tưởng tượng một tệp có bản ghi trên mỗi dòng (ví dụ: tệp CSV). Và máy tính đã viết hồ sơ ở cuối tập tin. Nhưng nó bất ngờ bị rơi. Gee là dòng cuối cùng hoàn thành? (không phải là một tình huống tốt đẹp)

Nhưng nếu chúng ta luôn chấm dứt dòng cuối cùng, thì chúng ta sẽ biết (chỉ cần kiểm tra xem dòng cuối cùng có bị chấm dứt không). Nếu không, chúng tôi có thể phải loại bỏ dòng cuối cùng mỗi lần, để được an toàn.


10

Có lẽ chỉ đơn giản là một số mã phân tích dự kiến ​​nó sẽ ở đó.

Tôi không chắc chắn tôi sẽ coi đó là một "quy tắc", và nó chắc chắn không phải là thứ tôi tuân thủ một cách tôn giáo. Hầu hết các mã hợp lý sẽ biết cách phân tích văn bản (bao gồm mã hóa) theo từng dòng (bất kỳ lựa chọn kết thúc dòng nào), có hoặc không có dòng mới trên dòng cuối cùng.

Thật vậy - nếu bạn kết thúc bằng một dòng mới: liệu (về lý thuyết) có một dòng cuối cùng trống giữa EOL và EOF không? Một người suy ngẫm ...


12
Đó không phải là một quy tắc, đó là một quy ước: một dòng là một cái gì đó kết thúc bằng một dòng cuối . Vì vậy, không có "dòng cuối cùng trống" giữa EOL và EOF.
MestreLion

4
@MestreLion: Nhưng nhân vật trong câu hỏi không được đặt tên là "dòng cuối", nó được đặt tên là "dòng mới" và / hoặc "nguồn cấp dữ liệu". Một dấu phân cách dòng, không phải là dấu kết thúc dòng. Và kết quả là một dòng trống cuối cùng.
Ben Voigt

2
Không có công cụ (lành mạnh) nào sẽ tính EOL cuối cùng (CR, LF, v.v.) của một tệp dưới dạng một dòng trống bổ sung. Và tất cả các công cụ POSIX sẽ không tính các ký tự cuối cùng của tệp dưới dạng một dòng nếu không có EOL kết thúc. Bất kể tên ký tự EOL là "nguồn cấp dữ liệu" hay "trả lại vận chuyển" (không có ký tự có tên là "dòng mới"), đối với tất cả các công cụ cảm nhận thực tế của con rối đều coi nó như một bộ kết thúc dòng , không phải là dấu phân cách dòng .
MestreLion

2
@MestreLion, Bạn có chắc chắn "dòng kết thúc" là lành mạnh? Lấy một vài người không lập trình và làm một cuộc khảo sát nhanh. Bạn sẽ nhanh chóng nhận ra khái niệm đường gần với khái niệm "đường phân cách". Khái niệm "dòng kết thúc" là lạ .
Pacerier

4
@Sahuagin: Đây không phải là quan điểm của tôi , đây là cách Tiêu chuẩn POSIX định nghĩa một dòng. Một tập tin rỗng với 0 byte có 0 dòng, vì thế không có EOL, và một tập tin được coi là có chỉ là một, dòng trống duy nhất, nó không đòi hỏi một EOL. Cũng lưu ý rằng điều này chỉ có liên quan nếu bạn muốn đếm các dòng trên một tệp, vì rõ ràng bất kỳ trình soạn thảo nào cũng sẽ cho phép bạn "nhận" đến dòng tiếp theo (hoặc đầu tiên) bất kể đã có EOL ở đó chưa.
MestreLion

10

Cuối cùng cũng có một vấn đề lập trình thực tế với các tệp thiếu dòng mới: readBash tích hợp (Tôi không biết về các readtriển khai khác ) không hoạt động như mong đợi:

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

Bản in này thôifoo ! Lý do là khi readgặp dòng cuối cùng, nó ghi nội dung vào $linenhưng trả về mã thoát 1 vì nó đạt EOF. Điều này phá vỡ whilevòng lặp, vì vậy chúng tôi không bao giờ đạt được echo $linemột phần. Nếu bạn muốn xử lý tình huống này, bạn phải làm như sau:

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

Đó là, làm echonếu readthất bại vì một dòng không trống ở cuối tập tin. Đương nhiên, trong trường hợp này sẽ có thêm một dòng mới trong đầu ra không có trong đầu vào.


9

Tại sao các tệp (văn bản) nên kết thúc bằng một dòng mới?

Cũng được thể hiện bởi nhiều người, bởi vì:

  1. Nhiều chương trình không hoạt động tốt, hoặc thất bại mà không có nó.

  2. Ngay cả các chương trình xử lý tốt tệp không có kết thúc '\n', chức năng của công cụ có thể không đáp ứng mong đợi của người dùng - điều này có thể không rõ ràng trong trường hợp góc này.

  3. Các chương trình hiếm khi không cho phép cuối cùng '\n'(tôi không biết về bất kỳ).


Tuy nhiên, điều này đặt ra câu hỏi tiếp theo:

Mã nên làm gì về các tệp văn bản mà không có dòng mới?

  1. Quan trọng nhất - Không viết mã giả sử tệp văn bản kết thúc bằng một dòng mới . Giả sử một tệp phù hợp với định dạng dẫn đến hỏng dữ liệu, tấn công và tấn công của hacker. Thí dụ:

    // 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ếu dấu vết cuối cùng '\n'là cần thiết, cảnh báo người dùng về sự vắng mặt của nó và hành động được thực hiện. IOWs, xác nhận định dạng của tập tin. Lưu ý: Điều này có thể bao gồm giới hạn về độ dài dòng tối đa, mã hóa ký tự, v.v.

  3. Xác định rõ ràng, tài liệu, xử lý mã của một trận chung kết bị thiếu '\n'.

  4. Đừng, càng tốt, tạo một tập tin thiếu kết thúc '\n'.


4

Ở đây rất muộn nhưng tôi chỉ gặp phải một lỗi trong quá trình xử lý tệp và điều đó xảy ra do các tệp không kết thúc với dòng mới trống. Chúng tôi đã xử lý các tệp văn bản với sedsedđã bỏ qua dòng cuối cùng từ đầu ra, điều này gây ra cấu trúc json không hợp lệ và khiến phần còn lại của quá trình bị lỗi.

Tất cả những gì chúng tôi đã làm là:

Có một tệp mẫu cho biết: foo.txtvới một số jsonnội dung bên trong nó.

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

Tệp được tạo trong các góa phụ máy và các kịch bản cửa sổ đang xử lý tệp đó bằng các lệnh PowerShell. Tất cả đều tốt.

Khi chúng tôi xử lý cùng một tệp bằng sedlệnhsed 's|value|newValue|g' foo.txt > foo.txt.tmp

Các tập tin mới được tạo là

[{
    someProp: value
},
{
    someProp: value

và bùng nổ, nó đã thất bại trong các quy trình còn lại vì JSON không hợp lệ.

Vì vậy, nó luôn luôn là một thực hành tốt để kết thúc tệp của bạn với dòng mới trống.


3

Tôi luôn có ấn tượng rằng quy tắc xuất phát từ những ngày khi phân tích một tệp mà không có dòng mới kết thúc là khó khăn. Đó là, bạn sẽ kết thúc việc viết mã trong đó một dòng cuối được xác định bởi ký tự EOL hoặc EOF. Nó chỉ đơn giản hơn khi giả sử một dòng kết thúc bằng EOL.

Tuy nhiên tôi tin rằng quy tắc này bắt nguồn từ trình biên dịch C yêu cầu dòng mới. Và như đã chỉ ra trên mạng Không có dòng mới nào ở cuối tập tin cảnh báo trình biên dịch , #include sẽ không thêm dòng mới.


0

Hãy tưởng tượng rằng tệp đang được xử lý trong khi tệp vẫn đang được tạo bởi một quy trình khác.

Nó có thể phải làm gì với điều đó? Một cờ cho biết rằng tệp đã sẵn sàng để được xử lý.


-4

Cá nhân tôi thích các dòng mới ở cuối các tệp mã nguồn.

Nó có thể có nguồn gốc với Linux hoặc tất cả các hệ thống UNIX cho vấn đề đó. Tôi nhớ có lỗi biên dịch (gcc nếu tôi không nhầm) vì các tệp mã nguồn không kết thúc bằng một dòng mới trống. Tại sao nó lại được làm theo cách này để người ta tự hỏi.


-6

IMHO, đó là vấn đề về phong cách và quan điểm cá nhân.

Vào thời xa xưa, tôi đã không đặt dòng mới đó. Một ký tự được lưu có nghĩa là tốc độ cao hơn thông qua modem 14,4K đó.

Sau đó, tôi đặt dòng mới đó để dễ dàng chọn dòng cuối cùng bằng cách sử dụng shift + downarrow.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.