Perintah "wc -c" dan "wc -m" di linux

24

Saya punya file teks, isinya:

i k k

Ketika saya gunakan wc -muntuk menghitung nomor karakter pada file ini, hasilnya adalah 7 .

Pertanyaan 1: Tapi mengapa saya mendapatkan 7, bukankah seharusnya saya mendapatkan " 6 " seandainya itu menghitung karakter " end-of-line "?

Pertanyaan 2: Bagaimana tepatnya cara wc -mkerjanya?

Pertanyaan 3: Ketika saya menggunakan wc -c(untuk menghitung angka byte), saya memiliki hasil yang sama dengan wc -m, jadi apa gunanya memiliki dua opsi yang berbeda ? Mereka melakukan pekerjaan yang persis sama, bukan? Jika tidak, apa bedanya dan bagaimana cara wc -ckerjanya?

SWIIWII
sumber
1
Anda juga bisa mendapatkan 7 jika Anda mengajukan datang dari Windows dengan ujung garis CRLF
Chris H

Jawaban:

36

Anda seharusnya hanya memiliki 6 karakter di sana. Coba jalankan

cat -A filename

Untuk melihat karakter non-cetak file Anda. Anda harus memiliki sesuatu yang ekstra. Jika saya membuat file seperti milik Anda, saya mengerti

i k k$

Apakah Anda menaruh spasi? Itu akan menghasilkan 7: i k k $atau mungkin memiliki baris baru:

i k k$
$

yang juga 7

Seperti yang Anda katakan

wc -m

menghitung karakter dan

wc -c

menghitung byte. Jika semua karakter Anda adalah bagian dari rangkaian karakter ASCII, maka hanya akan ada 1 byte per karakter sehingga Anda akan mendapatkan jumlah yang sama dari kedua perintah.

Coba pada file dengan karakter non ASCII:

$ echo ك > testfile
$ wc -m testfile
2 testfile
$ wc -c testfile
3 testfile

Aha! Lebih banyak byte daripada karakter sekarang.

Zanna
sumber
3
Saya menggunakan perintah " cat -A " dan akhirnya saya menemukan satu spasi sebelum karakter " end-of-line " ( $ ). Itu sebabnya saya mendapat 7 bukannya 6. Terima kasih, " kucing-A " banyak membantu.
SWIIWII
2
@SWIIWII Ya saya baru saja menambahkan itu ke jawaban saya karena saya pikir itu akan menjadi mungkin :)
Zanna
1
karakter baris baru juga dihitung. Bahkan jika itu semacam tidak terlihat, itu masih merupakan karakter dan dianggap dalam file sebagai potongan data. Omong-omong penggunaan kucing. Pernah juga bisa menggunakan hexdump atau xxd untuk melakukan hal yang sama
Sergiy Kolodyazhnyy
@Erger ya, dan cat -Aakan menunjukkan itu juga. Saya menambahkan jawaban saya, terima kasih :)
Zanna
@SWIIWII memasukkan kode di backticks `likethis`untuk membuatnya mudah dibaca, jangan membuatnya tebal
phuclv
2
$ locale charmap
UTF-8

Dalam lingkungan saya saat ini, set karakter adalah UTF-8, yaitu, karakter dikodekan dengan 1 hingga 4 byte per karakter (meskipun karena definisi asli dari UTF-8 memungkinkan kode karakter menunjukkan hingga 0x7fffffff, sebagian besar alat akan mengenali UTF- Urutan 8 byte hingga 6 byte).

Dalam set karakter itu, semua karakter dari Unicode tersedia, a adikodekan sebagai nilai byte 65, a sebagai 3 byte 228 185 149 dan ésebagai urutan dua byte 195 169 misalnya.

$ printf 乕 | wc -mc
  1       3
$ printf a | wc -mc
  1       1

Sekarang:

$ export fr_FR.iso885915@euro
$ locale charmap
ISO-8859-15

Saya telah memodifikasi lingkungan saya, di mana set karakter sekarang ISO-8859-15 (hal-hal lain seperti bahasa, simbol mata uang, format tanggal juga telah dimodifikasi, kumpulan pengaturan regional tersebut disebut sebagai lokal ). Saya perlu memulai emulator terminal baru di lingkungan itu untuk mengadaptasi render karakternya ke lokal baru.

ISO-8859-15 adalah set karakter byte tunggal yang berarti hanya memiliki 256 karakter (sebenarnya bahkan lebih sedikit dari yang sebenarnya tercakup). Kumpulan karakter tertentu digunakan untuk bahasa Eropa Barat karena mencakup sebagian besar bahasa (dan simbol euro).

Ia memiliki akarakter dengan nilai byte 65 seperti pada UTF-8 atau ASCII, ia juga memiliki ékarakter (seperti yang biasa digunakan dalam bahasa Prancis atau Spanyol misalnya) tetapi dengan nilai byte 233, ia tidak memiliki karakter 乕.

Di lingkungan itu, wc -cdan wc -makan selalu memberikan hasil yang sama.

Di Ubuntu seperti pada kebanyakan sistem mirip Unix modern, standarnya biasanya UTF-8 karena hanya set karakter yang didukung (dan penyandian) yang mencakup seluruh jangkauan Unicode.

Pengkodean karakter multi-byte lain ada, tetapi tidak didukung dengan baik di Ubuntu dan Anda harus melalui lingkaran untuk dapat menghasilkan lokal dengan itu, dan jika Anda melakukannya, Anda akan menemukan bahwa banyak hal tidak bekerja dengan benar.

Jadi berlaku di Ubuntu, set karakter dapat berupa byte tunggal, atau UTF-8.

Sekarang, beberapa catatan lagi:

Dalam UTF-8, tidak semua urutan byte membentuk karakter yang valid. Sebagai contoh, semua karakter UTF-8 yang bukan ASCII dibentuk dengan byte yang semuanya memiliki bit ke-8, tetapi hanya karakter pertama yang memiliki bit ke-7.

Jika Anda memiliki urutan byte dengan set bit ke-8, tidak ada yang memiliki set bit ke-7, maka itu tidak dapat diterjemahkan ke karakter. Dan saat itulah Anda mulai mengalami masalah dan inkonsistensi karena perangkat lunak tidak tahu apa yang harus dilakukan dengan itu. Contohnya:

$ printf '\200\200\200' | wc -mc
      0       3
$ printf '\200\200\200' | grep -q . || echo no
no

wcdan greptidak menemukan karakter di sana tetapi:

$ x=$'\200\200\200' bash -c 'echo "${#x}"'
3

bash menemukan 3. Ketika itu tidak dapat memetakan urutan byte ke karakter, ia menganggap setiap byte sebagai karakter.

Itu bisa menjadi lebih rumit karena ada titik-titik kode dalam Unicode yang tidak valid sebagai karakter, dan beberapa yang non-karakter , dan tergantung pada alatnya, pengkodean UTF-8 mereka mungkin atau mungkin tidak dianggap sebagai karakter.

Hal lain yang perlu dipertimbangkan adalah perbedaan antara karakter dan graphem, dan bagaimana mereka ditampilkan.

$ printf 'e\u301\u20dd\n'
é⃝
$ printf 'e\u301\u20dd' | wc -mc
      3       6

Di sana, kami telah mengkode 3 karakter sebagai 6 byte yang dirender sebagai satu graphem, karena kami memiliki 3 karakter yang digabungkan menjadi satu (satu karakter dasar, sebuah aksen akut kombinasi, dan sebuah lingkaran penutup yang mengombinasikan).

Implementasi GNU wcseperti yang ditemukan di Ubuntu memiliki -Lsaklar untuk memberi tahu Anda lebar tampilan garis terluas di input:

$ printf 'e\u301\u20dd\n' | wc -L
1

Anda juga akan menemukan bahwa beberapa karakter menempati 2 sel dalam perhitungan lebar seperti karakter kami dari atas:

$ echo 乕 | wc -L
2

Kesimpulannya: dalam kata yang lebih liar, byte, karakter dan graphem tidak harus sama.

Stéphane Chazelas
sumber
1

Perbedaan antara wc -cdan wc -madalah bahwa di lokal dengan karakter multibyte (katakanlah, UTF8), yang pertama menghitung byte, sedangkan yang kedua menghitung karakter. Pertimbangkan file berikut:

$ hexdump -C dummy.txt 
00000000  78 79 cf 80 0a                                    |xy...|

(bagi mereka yang tidak berbicara UTF8, itu huruf 'x', 'y', dan 'π', diikuti oleh baris baru). Panjangnya lima byte:

$ wc -c dummy.txt 
5 dummy.txt

tetapi hanya empat karakter:

$ wc -m dummy.txt 
4 dummy.txt
Menandai
sumber
Atau, pertimbangkan bahkan UTF-32 di mana setiap karakter memiliki 4 byte.
Jörg W Mittag