Điểm nào trong việc thêm một dòng mới vào cuối tập tin?


166

Một số trình biên dịch (đặc biệt là các trình biên dịch C hoặc C ++) cung cấp cho bạn các cảnh báo về:

No new line at end of file

Tôi nghĩ rằng đây sẽ là một vấn đề chỉ dành cho lập trình viên C, nhưng github hiển thị một thông báo trong chế độ xem cam kết:

\ No newline at end of file

cho một tệp PHP.

Tôi hiểu điều tiền xử lý được giải thích trong luồng này , nhưng điều này có liên quan gì đến PHP? Đây có phải là điều tương include()tự hoặc nó có liên quan đến chủ đề \r\nvs \n?

Điểm có một dòng mới ở cuối tập tin là gì?



2
Để chọc giận mọi người.
Andrew

4
Nếu bạn catlà tệp, lời nhắc tiếp theo sẽ được thêm vào "dòng" cuối cùng nếu nó không kết thúc bằng một dòng mới.
Aaron Franke

Câu trả lời:


188

Đây không phải là thêm một dòng mới vào cuối tập tin, mà là về việc không xóa dòng mới sẽ có ở đó.

Một tệp văn bản , dưới unix, bao gồm một loạt các dòng , mỗi dòng kết thúc bằng một ký tự dòng mới ( \n). Do đó, một tệp không trống và không kết thúc bằng một dòng mới không phải là một tệp văn bản.

Các tiện ích được cho là hoạt động trên các tệp văn bản có thể không phù hợp với các tệp không kết thúc bằng một dòng mới; các tiện ích Unix lịch sử có thể bỏ qua văn bản sau dòng mới nhất, ví dụ. Các tiện ích GNU có chính sách ứng xử khéo léo với các tệp không phải là văn bản và hầu hết các tiện ích hiện đại khác cũng vậy, nhưng bạn vẫn có thể gặp phải hành vi kỳ lạ với các tệp bị thiếu một dòng mới cuối cùng¹.

Với GNU diff, nếu một trong các tệp được so sánh kết thúc bằng một dòng mới nhưng không phải là tệp khác, thì cần lưu ý thực tế đó. Vì diff được định hướng theo dòng, nên nó không thể chỉ ra điều này bằng cách lưu trữ một dòng mới cho một trong các tệp nhưng không phải cho các tệp khác - các dòng mới là cần thiết để chỉ ra nơi mỗi dòng trong tệp diff bắt đầu và kết thúc. Vì vậy, diff sử dụng văn bản đặc biệt này \ No newline at end of fileđể phân biệt một tệp không kết thúc trong một dòng mới với một tệp đã làm.

Nhân tiện, trong ngữ cảnh C, một tệp nguồn tương tự bao gồm một loạt các dòng. Chính xác hơn, một đơn vị dịch thuật được xem trong một triển khai - được định nghĩa là một chuỗi các dòng, mỗi dòng phải kết thúc bằng một ký tự dòng mới ( n1256 §5.1.1.1). Trên các hệ thống unix, ánh xạ rất đơn giản. Trên DOS và Windows, mỗi chuỗi CR LF ( \r\n) được ánh xạ tới một dòng mới ( \n; đây là điều luôn xảy ra khi đọc tệp được mở dưới dạng văn bản trên các HĐH này). Có một số HĐH không có ký tự dòng mới, nhưng thay vào đó có các bản ghi có kích thước cố định hoặc có thể thay đổi; trên các hệ thống này, ánh xạ từ tệp đến nguồn C giới thiệu một\nvào cuối mỗi bản ghi. Mặc dù điều này không liên quan trực tiếp đến unix, nhưng điều đó có nghĩa là nếu bạn sao chép tệp nguồn C thiếu dòng mới cuối cùng của nó sang hệ thống có tệp văn bản dựa trên bản ghi, sau đó sao chép lại, bạn sẽ kết thúc với phần chưa hoàn chỉnh dòng cuối cùng bị cắt bớt trong chuyển đổi ban đầu hoặc một dòng mới bổ sung được xử lý trong quá trình chuyển đổi ngược lại.

¹ Ví dụ: đầu ra của GNU loại luôn luôn kết thúc bằng một dòng mới. Vì vậy, nếu tệp foobị thiếu dòng mới cuối cùng, bạn sẽ thấy sort foo | wc -cbáo cáo có nhiều ký tự hơn cat foo | wc -c.


Liên quan đến "... hàng loạt dòng, mỗi dòng phải kết thúc bằng một ký tự dòng mới (n1256 §5.1.1.1)" -> Khi xem lại một chiếc C11dr N1570 gần đây, không tìm thấy sự hỗ trợ nào khác ngoài có thể: "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 trước khi xảy ra bất kỳ kết nối nào như vậy." §5.1.1.2 2, nhưng dường như bị hạn chế đối với thông số kỹ thuật nối.
chux

@chux Câu đó cũng có trong n1256. Dòng cuối cùng phải kết thúc bằng một ký tự dòng mới. Các dòng không phải là dòng cuối cùng rõ ràng cũng phải kết thúc bằng một ký tự dòng mới để chỉ ra rằng dòng đó kết thúc và dòng tiếp theo bắt đầu. Do đó, mỗi dòng phải kết thúc bằng một ký tự dòng mới.
Gilles

Hmmm, với tôi, dòng đó "" Một tập tin nguồn ... việc ghép nối diễn ra. "Có thể bị giới hạn ở cách xem xét ghép nối và không phải các tập tin nói chung. Tuy nhiên, tôi thấy cách người ta có thể xem khác. tập trung vào đó.
chux

> "Vì vậy, diff sử dụng văn bản đặc biệt này \ Không có dòng mới nào ở cuối tệp để phân biệt một tệp không kết thúc trong một dòng mới với một tệp đã làm." Git hiển thị văn bản này không chỉ khi nó so sánh các tập tin. Nhưng ngay cả khi tập tin mới được thêm vào git. Vì vậy, lập luận này không hợp lệ, tôi cho rằng.
Viktor Kruglikov

> "Các tiện ích được cho là hoạt động trên các tệp văn bản có thể không phù hợp với các tệp không kết thúc bằng một dòng mới" Tôi không nghĩ rằng đó là việc của git để quan tâm đến các vấn đề cấp thấp như vậy vì thiếu \ n vì POSIX yêu cầu. Tôi nghĩ rằng nếu git hiển thị thông báo này, lý do nên nằm trong các vấn đề kiểm soát nguồn .
Viktor Kruglikov

41

Không nhất thiết là lý do, nhưng hậu quả thực tế của các tệp không kết thúc bằng một dòng mới:

Xem xét những gì sẽ xảy ra nếu bạn muốn xử lý một số tệp bằng cách sử dụng cat. Chẳng hạn, nếu bạn muốn tìm từ fooở đầu dòng trên 3 tệp:

cat file1 file2 file3 | grep -e '^foo'

Nếu dòng đầu tiên trong tệp 3 bắt đầu bằng foo, nhưng tệp 2 không có dòng cuối cùng \nsau dòng cuối cùng của nó, thì sự xuất hiện này sẽ không được tìm thấy bởi grep, bởi vì dòng cuối cùng trong tệp 2 và dòng đầu tiên trong tệp 3 sẽ được grep xem là một hàng.

Vì vậy, để thống nhất và để tránh những điều bất ngờ, tôi cố gắng giữ cho các tệp của mình luôn kết thúc bằng một dòng mới.


Nhưng nó có phải là kinh doanh của git để quan tâm đến tập tin nối?
Viktor Kruglikov

Không phải lý do là bạn chỉ nên đưa '\n'hoạt động của mèo vào ...
Andrew

3
Điều đó giống như nói, "Đôi khi tôi nối các chuỗi với nhau có \nhoặc khoảng trắng ở cuối, vì vậy để giữ mọi thứ nhất quán, tôi luôn đặt \n _____ở hai đầu của chuỗi." Chà, không, điều đúng đắn cần làm là cắt dây của bạn và sau đó nối chúng đúng cách.
Andrew

16

Có hai khía cạnh:

  1. Có / đã có một số trình biên dịch C không thể phân tích cú pháp dòng cuối cùng nếu nó không kết thúc bằng một dòng mới. Tiêu chuẩn C chỉ định rằng tệp C sẽ kết thúc bằng một dòng mới (C11, 5.1.1.2, 2.) và một dòng cuối cùng không có dòng mới mang lại hành vi không xác định (mục C11, J.2, mục 2). Có lẽ vì lý do lịch sử, bởi vì một số nhà cung cấp trình biên dịch như vậy là một phần của ủy ban khi tiêu chuẩn đầu tiên được viết. Do đó, cảnh báo của GCC.

  2. diffcác chương trình (như được sử dụng bởi git diff, github, v.v.) hiển thị từng dòng khác nhau giữa các tệp. Họ thường in một tin nhắn khi chỉ một tập tin kết thúc bằng một dòng mới bởi vì bạn sẽ không thấy sự khác biệt này. Ví dụ: nếu sự khác biệt duy nhất giữa hai tệp là sự hiện diện của ký tự dòng mới nhất, nếu không có gợi ý thì có vẻ như cả hai tệp đều giống nhau, khi diffcmptrả về mã thoát thành công không đồng đều và tổng kiểm tra của các tệp (ví dụ: thông qua md5sum) không phù hợp.


có ý nghĩa với chương trình diff
Thamaraiselvam

Âm thanh như diffs chỉ nên thông minh hơn.
Andrew

@Andrew, không, nó không. diffdự kiến ​​sẽ in sự khác biệt nếu có bất kỳ. Và nếu một tệp có một dòng mới là ký tự cuối cùng trong khi tệp kia thì không có sự khác biệt đó bằng cách nào đó đáng chú ý trong đầu ra.
maxschlepzig

Tuyên bố sau của bạn là chính xác. Tuy nhiên, trình xem khác không phải hiển thị "dòng mới" ( \n) để bắt đầu, thay vào đó, nó có thể chỉ hiển thị "dòng mới".
Andrew

10

Các \ No newline at end of filebạn nhận được từ github xuất hiện ở phần cuối của một bản vá (trong diffđịnh dạng , xem lưu ý ở phần cuối của phần "Unified Format").

Trình biên dịch không quan tâm liệu có dòng mới hay không ở cuối tệp, nhưng git(và diff/ patchtiện ích) phải đưa những tài khoản đó vào tài khoản. Có nhiều lý do cho điều đó. Ví dụ: việc quên thêm hoặc xóa một dòng mới ở cuối tệp sẽ thay đổi hàm băm của nó ( md5sum/ sha1sum). Ngoài ra, các tệp không phải luôn luôn là chương trình và cuối cùng \ncó thể tạo ra một số khác biệt.

Lưu ý : Về cảnh báo từ trình biên dịch C, tôi đoán họ khăng khăng cho một dòng mới cuối cùng cho mục đích tương thích ngược. Trình biên dịch rất cũ có thể không chấp nhận dòng cuối cùng nếu không kết thúc bằng \n(hoặc chuỗi char cuối dòng phụ thuộc hệ thống khác).


7
"Tôi đoán họ khăng khăng cho một dòng mới cuối cùng cho mục đích tương thích ngược" - Không, họ nhấn mạnh vào nó bởi vì tiêu chuẩn C bắt buộc nó.
MestreLion

1
@MestreLion C yêu cầu một dòng mới cuối cùng cho mã nguồn C (C11 §5.1.1.2 2). Lưu ý rằng đối với I / O tệp văn bản , C có "Dòng cuối cùng có yêu cầu ký tự dòng mới kết thúc được xác định theo thực hiện hay không." §7,21.2 2
chux

Ai đang sử dụng trình biên dịch rất cũ? Ngừng sử dụng chúng.
Andrew

1
@MestreLion: Và tại sao bạn nghĩ rằng tiêu chuẩn C bắt buộc nó
Stéphane Gimenez

@ StéphaneGimenez: tính nhất quán, khả năng tương thích và khả năng tương tác tốt hơn giữa các hệ điều hành khác nhau (POSIX cũng xác định các dòng kết thúc bằng '\ n')
MestreLion

4

POSIX, đây là một bộ tiêu chuẩn được chỉ định bởi IEEE để duy trì khả năng tương thích giữa các hệ điều hành.

Một trong số đó là định nghĩa của "dòng" là một chuỗi gồm 0 hoặc nhiều ký tự không cộng với ký tự dòng mới kết thúc.

Vì vậy, để dòng cuối cùng được công nhận là một "dòng" thực sự, nó phải có một ký tự dòng mới kết thúc.

Điều này rất quan trọng nếu bạn phụ thuộc vào các công cụ hệ điều hành để nói số lượng dòng hoặc tách / trợ giúp phân tích tệp của bạn. Với PHP là một ngôn ngữ kịch bản, nó hoàn toàn có thể, đặc biệt là trong những ngày đầu hoặc ngay cả bây giờ (tôi không có ý tưởng / định đề) nó có các phụ thuộc hệ điều hành như thế.

Trong thực tế, hầu hết các hệ điều hành không hoàn toàn tuân thủ POSIX và con người không thích máy đó hoặc thậm chí quan tâm đến việc chấm dứt các dòng mới. Vì vậy, đối với hầu hết mọi thứ, nó là một bữa tiệc tất cả mọi thứ quan tâm đến nó, cảnh báo hoặc chỉ cần đi một chút văn bản cuối cùng thực sự là một dòng vì vậy chỉ cần bao gồm nó.


3

Ngoài ra còn có quan điểm giữ lịch sử khác biệt. Nếu một tệp kết thúc mà không có ký tự dòng mới, thì việc thêm bất cứ thứ gì vào cuối tệp sẽ được xem bởi các tiện ích khác như thay đổi dòng cuối cùng (vì \nđang được thêm vào nó).

Điều này có thể gây ra kết quả không mong muốn với các lệnh như git blamehg annotate.


Âm thanh như diffs chỉ cần thông minh hơn.
Andrew
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.