Saya berasumsi semua orang di sini terbiasa dengan pepatah bahwa semua file teks harus diakhiri dengan baris baru. Saya sudah tahu tentang "aturan" ini selama bertahun-tahun tetapi saya selalu bertanya-tanya - mengapa?
Saya berasumsi semua orang di sini terbiasa dengan pepatah bahwa semua file teks harus diakhiri dengan baris baru. Saya sudah tahu tentang "aturan" ini selama bertahun-tahun tetapi saya selalu bertanya-tanya - mengapa?
Jawaban:
Karena begitulah standar POSIX mendefinisikan garis :
- 3.206 Baris
- Urutan nol atau lebih karakter <newline> plus karakter terminasi <newline>.
Oleh karena itu, garis yang tidak diakhiri dengan karakter baris baru tidak dianggap sebagai garis yang sebenarnya. Itu sebabnya beberapa program mengalami masalah dalam memproses baris terakhir file jika tidak diakhiri baris baru.
Ada setidaknya satu keuntungan sulit untuk pedoman ini ketika bekerja pada emulator terminal: Semua alat Unix mengharapkan konvensi ini dan bekerja dengannya. Misalnya, ketika menggabungkan file dengan cat
, file yang diakhiri oleh baris baru akan memiliki efek yang berbeda dari yang tanpa:
$ more a.txt
foo
$ more b.txt
bar$ more c.txt
baz
$ cat {a,b,c}.txt
foo
barbaz
Dan, seperti contoh sebelumnya juga menunjukkan, ketika menampilkan file pada baris perintah (misalnya via more
), file yang diakhiri baris baru menghasilkan tampilan yang benar. File yang dihentikan secara tidak benar mungkin rusak (baris kedua).
Untuk konsistensi, sangat membantu untuk mengikuti aturan ini - melakukan hal lain akan membuat pekerjaan tambahan ketika berhadapan dengan alat Unix default.
Pikirkan secara berbeda: Jika baris tidak diakhiri oleh baris baru, membuat perintah seperti cat
berguna jauh lebih sulit: bagaimana Anda membuat perintah untuk menggabungkan file sedemikian rupa sehingga
b.txt
dan c.txt
?Tentu saja ini dapat dipecahkan tetapi Anda perlu membuat penggunaan yang cat
lebih kompleks (dengan menambahkan argumen baris perintah posisional, misalnya cat a.txt --no-newline b.txt c.txt
), dan sekarang perintah daripada masing-masing file individu mengontrol bagaimana ia ditempelkan bersama dengan file lain. Ini hampir pasti tidak nyaman.
... Atau Anda perlu memperkenalkan karakter penjaga khusus untuk menandai garis yang seharusnya dilanjutkan daripada dihentikan. Nah, sekarang Anda terjebak dengan situasi yang sama seperti pada POSIX, kecuali terbalik (kelanjutan garis daripada karakter pemutusan garis).
Sekarang, pada sistem yang tidak sesuai dengan POSIX (saat ini sebagian besar adalah Windows), intinya adalah dapat diperdebatkan: file umumnya tidak berakhir dengan baris baru, dan definisi (informal) dari sebuah baris misalnya “teks yang dipisahkan oleh baris baru” (perhatikan penekanannya). Ini sepenuhnya valid. Namun, untuk data terstruktur (misalnya kode pemrograman) itu membuat parsing minimal lebih rumit: umumnya berarti parser harus ditulis ulang. Jika parser pada awalnya ditulis dengan definisi POSIX dalam pikiran, maka mungkin lebih mudah untuk memodifikasi aliran token daripada parser - dengan kata lain, tambahkan token "baris baru buatan" ke akhir input.
cat
cara yang bermanfaat dan konsisten.
Setiap baris harus diakhiri dalam karakter baris baru, termasuk yang terakhir. Beberapa program memiliki masalah dalam memproses baris terakhir file jika tidak dihentikan baris baru.
GCC memperingatkannya bukan karena tidak dapat memproses file, tetapi karena itu harus sebagai bagian dari standar.
Standar bahasa C mengatakan file sumber yang tidak kosong harus diakhiri dengan karakter baris baru, yang tidak akan segera didahului oleh karakter backslash.
Karena ini adalah klausa "wajib", kita harus memancarkan pesan diagnostik untuk pelanggaran aturan ini.
Ini ada di bagian 2.1.1.2 dari standar ANSI C 1989. Bagian 5.1.1.2 dari standar ISO C 1999 (dan mungkin juga standar ISO C 1990).
Referensi: Arsip surat GCC / GNU .
wc -l
tidak akan menghitung baris terakhir file jika bukan baris baru dihentikan. Juga, cat
akan bergabung dengan baris terakhir file dengan baris pertama file berikutnya menjadi satu jika baris terakhir dari file pertama tidak diakhiri baris baru. Hampir semua program yang mencari baris baru sebagai pembatas berpotensi mengacaukannya.
wc
telah telah disebutkan ....
cat
dan wc
)?
Jawaban ini merupakan upaya jawaban teknis daripada pendapat.
Jika kita ingin menjadi purix POSIX, kita mendefinisikan sebuah baris sebagai:
Urutan nol atau lebih karakter <newline> plus karakter terminasi <newline>.
Sumber: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206
Baris tidak lengkap sebagai:
Urutan satu atau lebih karakter bukan <newline> di akhir file.
Sumber: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195
File teks sebagai:
File yang berisi karakter yang disusun dalam nol atau lebih baris. Baris tidak mengandung karakter NUL dan panjangnya tidak boleh melebihi {LINE_MAX} byte, termasuk karakter <newline>. Meskipun POSIX.1-2008 tidak membedakan antara file teks dan file biner (lihat standar ISO C), banyak utilitas hanya menghasilkan output yang dapat diprediksi atau bermakna ketika beroperasi pada file teks. Utilitas standar yang memiliki batasan seperti itu selalu menentukan "file teks" di bagian STDIN atau INPUT FILES.
Sumber: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_397
Sebuah string sebagai:
Urutan byte yang berdekatan diakhiri oleh dan termasuk byte nol pertama.
Sumber: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396
Dari sini, kita dapat memperoleh bahwa satu-satunya waktu kita berpotensi akan menghadapi semua jenis masalah adalah jika kita berurusan dengan konsep garis file atau file sebagai file teks (adalah bahwa file teks adalah organisasi nol atau lebih banyak baris, dan baris yang kita tahu harus diakhiri dengan <newline>).
Contoh kasus: wc -l filename
.
Dari wc
manual yang kita baca:
Baris didefinisikan sebagai string karakter yang dibatasi oleh karakter <newline>.
Apa implikasinya terhadap file JavaScript, HTML, dan CSS kemudian menjadi file teks ?
Di browser, IDE modern, dan aplikasi front-end lainnya tidak ada masalah dengan melewatkan EOL di EOF. Aplikasi akan mem-parsing file dengan benar. Itu harus karena tidak semua Sistem Operasi sesuai dengan standar POSIX, sehingga akan menjadi tidak praktis untuk alat-alat non-OS (misalnya browser) untuk menangani file sesuai dengan standar POSIX (atau standar level OS apa pun).
Sebagai hasilnya, kita dapat relatif yakin bahwa EOL di EOF hampir tidak akan memiliki dampak negatif pada tingkat aplikasi - terlepas dari apakah itu berjalan pada OS UNIX.
Pada titik ini kita dapat dengan yakin mengatakan bahwa melewatkan EOL di EOF adalah aman ketika berhadapan dengan JS, HTML, CSS di sisi klien. Sebenarnya, kita dapat menyatakan bahwa meminimalkan salah satu dari file-file ini, tidak mengandung <newline> aman.
Kita dapat mengambil satu langkah lebih jauh dan mengatakan bahwa sejauh menyangkut NodeJS, ia juga tidak dapat mematuhi standar POSIX yang dapat dijalankan di lingkungan yang tidak memenuhi POSIX.
Apa yang tersisa dengan kita? Perkakas tingkat sistem.
Ini berarti satu-satunya masalah yang mungkin timbul adalah dengan alat yang berupaya untuk mematuhi fungsionalitasnya ke semantik POSIX (misalnya definisi garis seperti yang ditunjukkan pada wc
).
Meski begitu, tidak semua cangkang akan secara otomatis mematuhi POSIX. Bash misalnya tidak default untuk perilaku POSIX. Ada saklar untuk mengaktifkannya: POSIXLY_CORRECT
.
Makanan yang dipikirkan tentang nilai EOL adalah <newline>: https://www.rfc-editor.org/old/EOLstory.txt
Tetap berada di jalur perkakas, untuk semua maksud dan tujuan praktis, mari kita pertimbangkan ini:
Mari kita bekerja dengan file yang tidak memiliki EOL. Pada penulisan ini file dalam contoh ini adalah JavaScript yang diperkecil tanpa 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
Perhatikan cat
ukuran file persis jumlah dari masing-masing bagian. Jika penggabungan file JavaScript adalah masalah untuk file JS, perhatian yang lebih tepat adalah memulai setiap file JavaScript dengan titik koma.
Seperti orang lain yang disebutkan di utas ini: bagaimana jika Anda ingin cat
dua file yang outputnya menjadi hanya satu baris, bukan dua? Dengan kata lain, cat
lakukan apa yang seharusnya dilakukan.
Satu man
- cat
satunya menyebutkan input pembacaan hingga EOF, bukan <newline>. Perhatikan bahwa -n
saklar cat
juga akan mencetak garis yang diakhiri non-<newline> (atau garis tidak lengkap ) sebagai garis - artinya penghitungan dimulai dari 1 (sesuai dengan man
.)
-n Beri nomor pada garis keluaran, mulai dari 1.
Sekarang kita mengerti bagaimana POSIX mendefinisikan sebuah garis , perilaku ini menjadi ambigu, atau benar-benar, tidak sesuai.
Memahami tujuan dan kepatuhan alat tertentu akan membantu dalam menentukan seberapa penting untuk mengakhiri file dengan EOL. Dalam C, C ++, Java (JARs), dll ... beberapa standar akan menentukan baris baru untuk validitas - tidak ada standar seperti itu untuk JS, HTML, CSS.
Misalnya, alih-alih menggunakan wc -l filename
satu dapat melakukannya awk '{x++}END{ print x}' filename
, dan yakinlah bahwa keberhasilan tugas tidak terancam oleh file yang mungkin ingin kami proses yang tidak kami tulis (mis. Perpustakaan pihak ketiga seperti JS yang diperkecil, kami curl
d) - kecuali kami maksudnya adalah benar-benar menghitung garis dalam arti yang sesuai dengan POSIX.
Kesimpulan
Akan ada beberapa kasus penggunaan kehidupan nyata di mana melewatkan EOL di EOF untuk file teks tertentu seperti JS, HTML, dan CSS akan memiliki dampak negatif - jika sama sekali. Jika kami mengandalkan <newline> yang ada, kami membatasi keandalan perkakas kami hanya untuk file yang kami buat dan membuka diri terhadap kemungkinan kesalahan yang diperkenalkan oleh file pihak ketiga.
Moral dari cerita: Perkakas insinyur yang tidak memiliki kelemahan mengandalkan EOL di EOF.
Jangan ragu untuk memposting kasus penggunaan karena berlaku untuk JS, HTML dan CSS di mana kita dapat memeriksa bagaimana melewatkan EOL memiliki efek buruk.
Ini mungkin terkait dengan perbedaan antara :
Jika setiap baris berakhir dengan end-of-line, ini menghindari, misalnya, menggabungkan dua file teks akan membuat baris terakhir dijalankan pertama kali ke baris pertama baris kedua.
Plus, seorang editor dapat memeriksa pada saat memuat apakah file berakhir di end-of-line, menyimpannya di opsi lokal 'eol', dan menggunakannya saat menulis file.
Beberapa tahun yang lalu (2005), banyak editor (ZDE, Eclipse, Scite, ...) memang "melupakan" EOL akhir itu, yang sangat tidak dihargai .
Bukan hanya itu, tetapi mereka menafsirkan EOL akhir itu secara tidak benar, sebagai 'mulai baris baru', dan benar-benar mulai menampilkan baris lain seolah-olah sudah ada.
Ini sangat terlihat dengan file teks 'tepat' dengan editor teks yang berperilaku baik seperti vim, dibandingkan dengan membukanya di salah satu editor di atas. Ini menampilkan baris tambahan di bawah baris terakhir file. Anda melihat sesuatu seperti ini:
1 first line
2 middle line
3 last line
4
Beberapa alat mengharapkan ini. Misalnya, wc
mengharapkan ini:
$ echo -n "Line not ending in a new line" | wc -l
0
$ echo "Line ending with a new line" | wc -l
1
wc
tidak mengharapkan ini, sebanyak itu hanya bekerja dalam definisi POSIX dari "garis" yang bertentangan dengan pemahaman intuitif kebanyakan orang tentang "garis".
wc -l
mencetak 1
dalam kedua kasus, tetapi beberapa orang mungkin mengatakan kasus kedua harus dicetak 2
.
\n
sebagai terminator garis, bukan sebagai pemisah garis, seperti POSIX / UNIX, maka mengharapkan case kedua untuk mencetak 2 benar-benar gila.
Pada dasarnya ada banyak program yang tidak akan memproses file dengan benar jika mereka tidak mendapatkan EOL EOF akhir.
GCC memperingatkan Anda tentang ini karena itu diharapkan sebagai bagian dari standar C. (bagian 5.1.1.2 rupanya)
Ini berasal dari hari-hari awal ketika terminal sederhana digunakan. Char baris baru digunakan untuk memicu 'flush' dari data yang ditransfer.
Hari ini, baris baru char tidak diperlukan lagi. Tentu, banyak aplikasi yang masih memiliki masalah jika baris baru tidak ada, tetapi saya menganggapnya sebagai bug pada aplikasi tersebut.
Namun jika Anda memiliki format file teks di mana Anda memerlukan baris baru, Anda mendapatkan verifikasi data sederhana sangat murah: jika file berakhir dengan baris yang tidak memiliki baris baru di akhir, Anda tahu file rusak. Dengan hanya satu byte tambahan untuk setiap baris, Anda dapat mendeteksi file yang rusak dengan akurasi tinggi dan hampir tidak ada waktu CPU.
Kasus penggunaan terpisah: ketika file teks Anda dikontrol versi (dalam kasus ini khusus di bawah git meskipun itu berlaku untuk orang lain juga). Jika konten ditambahkan ke akhir file, maka baris yang sebelumnya baris terakhir akan diedit untuk memasukkan karakter baris baru. Ini berarti bahwa blame
ing file untuk mengetahui kapan baris itu terakhir diedit akan menampilkan tambahan teks, bukan komit sebelum yang Anda benar-benar ingin lihat.
\n
). Masalah terpecahkan.
Selain alasan praktis di atas, tidak akan mengejutkan saya jika penggagas Unix (Thompson, Ritchie, et al.) Atau pendahulu Multics mereka menyadari bahwa ada alasan teoritis untuk menggunakan terminator garis daripada pemisah garis: Dengan garis terminator, Anda dapat menyandikan semua file baris yang mungkin. Dengan pemisah garis, tidak ada perbedaan antara file garis nol dan file yang berisi satu baris kosong; keduanya dikodekan sebagai file yang berisi karakter nol.
Jadi, alasannya adalah:
wc -l
tidak akan menghitung "garis" akhir jika tidak diakhiri dengan baris baru.cat
hanya berfungsi dan bekerja tanpa komplikasi. Itu hanya menyalin byte dari setiap file, tanpa perlu interpretasi. Saya tidak berpikir ada setara dengan DOS cat
. Menggunakan copy a+b c
akan berakhir dengan menggabungkan baris terakhir file a
dengan baris pertama fileb
.Saya sudah bertanya-tanya sendiri selama bertahun-tahun. Tapi saya menemukan alasan bagus hari ini.
Bayangkan sebuah file dengan catatan di setiap baris (mis: file CSV). Dan komputer itu sedang menulis catatan di akhir file. Tapi tiba-tiba jatuh. Wah apakah baris terakhir selesai? (bukan situasi yang baik)
Tetapi jika kita selalu mengakhiri baris terakhir, maka kita akan tahu (cukup periksa apakah baris terakhir dihentikan). Kalau tidak, kita mungkin harus membuang baris terakhir setiap kali, hanya untuk aman.
Mungkin hanya beberapa kode parsing yang diharapkan ada di sana.
Saya tidak yakin saya akan menganggapnya sebagai "aturan", dan tentu saja itu bukan sesuatu yang saya patuhi secara religius. Kode yang paling masuk akal akan tahu cara mem-parsing teks (termasuk penyandian) baris demi baris (semua pilihan akhir baris), dengan-atau-tanpa baris baru pada baris terakhir.
Memang - jika Anda mengakhiri dengan baris baru: apakah ada (secara teori) garis akhir kosong antara EOL dan EOF? Satu untuk direnungkan ...
Ada juga masalah pemrograman praktis dengan file yang tidak memiliki baris baru di akhir: read
Bash built-in (saya tidak tahu tentang read
implementasi lain ) tidak berfungsi seperti yang diharapkan:
printf $'foo\nbar' | while read line
do
echo $line
done
Ini hanyafoo
mencetak ! Alasannya adalah ketika read
menemukan baris terakhir, ia menulis konten $line
tetapi mengembalikan kode 1 karena mencapai EOF. Ini memutus while
perulangan, jadi kami tidak pernah mencapai echo $line
bagian itu. Jika Anda ingin menangani situasi ini, Anda harus melakukan hal berikut:
while read line || [ -n "${line-}" ]
do
echo $line
done < <(printf $'foo\nbar')
Yaitu, lakukan echo
jika read
gagal karena baris tidak kosong di akhir file. Secara alami, dalam hal ini akan ada satu baris tambahan baru dalam output yang tidak ada dalam input.
Mengapa file (teks) harus diakhiri dengan baris baru?
Seperti yang diungkapkan oleh banyak orang, karena:
Banyak program tidak berperilaku baik, atau gagal tanpanya.
Bahkan program yang menangani file dengan baik tidak memiliki akhiran '\n'
, fungsionalitas alat mungkin tidak memenuhi harapan pengguna - yang mungkin tidak jelas dalam kasus sudut ini.
Program jarang melarang final '\n'
(saya tidak tahu ada).
Namun ini menimbulkan pertanyaan berikutnya:
Apa yang harus dilakukan kode tentang file teks tanpa baris baru?
Paling penting - Jangan menulis kode yang menganggap file teks diakhiri dengan baris baru . Dengan asumsi file sesuai dengan format mengarah ke korupsi data, serangan hacker, dan crash. Contoh:
// 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
...
}
Jika trailing akhir '\n'
diperlukan, beri tahu pengguna jika tidak ada dan tindakan telah diambil. TKI, validasi format file. Catatan: Ini mungkin termasuk batas panjang garis maksimum, pengkodean karakter, dll.
Tentukan dengan jelas, dokumentasikan, penanganan kode dari final yang hilang '\n'
.
Jangan, sesering mungkin, menghasilkan file yang tidak memiliki akhir '\n'
.
Sangat terlambat di sini, tetapi saya hanya menghadapi satu bug dalam pemrosesan file dan itu datang karena file tidak berakhir dengan baris baru yang kosong. Kami sedang memproses file teks dengan sed
dansed
menghilangkan baris terakhir dari output yang menyebabkan struktur json tidak valid dan mengirimkan sisa proses ke keadaan gagal.
Yang kami lakukan adalah:
Ada satu file contoh mengatakan: foo.txt
dengan beberapa json
konten di dalamnya.
[{
someProp: value
},
{
someProp: value
}] <-- No newline here
File itu dibuat di mesin janda dan skrip jendela sedang memproses file itu menggunakan perintah PowerShell. Semuanya bagus.
Ketika kami memproses file yang sama menggunakan sed
perintahsed 's|value|newValue|g' foo.txt > foo.txt.tmp
File yang baru dibuat adalah
[{
someProp: value
},
{
someProp: value
dan boom, itu gagal seluruh proses karena JSON tidak valid.
Jadi selalu merupakan praktik yang baik untuk mengakhiri file Anda dengan baris baru yang kosong.
Saya selalu mendapat kesan bahwa aturan datang dari hari-hari ketika mem-parsing file tanpa mengakhiri baris baru itu sulit. Artinya, Anda akan berakhir menulis kode di mana ujung garis didefinisikan oleh karakter EOL atau EOF. Itu lebih sederhana untuk mengasumsikan garis yang diakhiri dengan EOL.
Namun saya percaya aturan ini diturunkan dari kompiler C yang membutuhkan baris baru. Dan seperti yang ditunjukkan pada peringatan kompiler "Tidak ada baris baru di akhir file" , #include tidak akan menambahkan baris baru.
Bayangkan file sedang diproses sementara file masih dibuat oleh proses lain.
Mungkin itu ada hubungannya dengan itu? Bendera yang menunjukkan bahwa file siap diproses.
Saya pribadi suka baris baru di akhir file kode sumber.
Ini mungkin berasal dari Linux atau semua sistem UNIX dalam hal ini. Saya ingat ada kesalahan kompilasi (gcc jika saya tidak salah) karena file kode sumber tidak diakhiri dengan baris baru yang kosong. Mengapa itu dibuat dengan cara ini membuat orang bertanya-tanya.
IMHO, ini masalah gaya dan pendapat pribadi.
Di masa lalu, saya tidak memasukkan baris baru itu. Karakter yang disimpan berarti lebih cepat melalui modem 14.4K itu.
Kemudian, saya meletakkan baris baru itu sehingga lebih mudah untuk memilih baris terakhir menggunakan shift + downarrow.