Bagaimana membuat tr menyadari karakter non-ascii (unicode)?

36

Saya mencoba untuk menghapus beberapa karakter dari file (UTF-8). Saya menggunakan truntuk tujuan ini:

tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat 

File berisi beberapa karakter asing (seperti "Латвийская" atau "àé"). trtampaknya tidak memahaminya: itu memperlakukan mereka sebagai non-alpha dan menghapus juga.

Saya telah mencoba mengubah beberapa pengaturan lokal saya:

LC_CTYPE=C LC_COLLATE=C tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat
LC_CTYPE=ru_RU.UTF-8 LC_COLLATE=C tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat
LC_CTYPE=ru_RU.UTF-8 LC_COLLATE=ru_RU.UTF-8 tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat

Sayangnya, tidak ada yang berhasil.

Bagaimana saya bisa trmengerti Unicode?

MatthewRock
sumber

Jawaban:

29

Itu yang diketahui ( 1 , 2 , 3 , 4 , 5 , 6 ) batasan implementasi GNU tr.

Ini tidak sebanyak itu tidak mendukung karakter asing , non-Inggris atau non-ASCII, tetapi tidak mendukung karakter multi-byte.

Karakter Cyrillic tersebut akan diperlakukan OK, jika ditulis dalam rangkaian karakter iso8859-5 (byte tunggal per karakter) (dan lokal Anda menggunakan charset itu), tetapi masalahnya adalah Anda menggunakan UTF-8 di mana bukan ASCII karakter dikodekan dalam 2 atau lebih byte.

GNU punya rencana (lihat juga ) untuk memperbaikinya dan pekerjaan sedang berjalan tetapi belum ada di sana.

FreeBSD atau Solaris trtidak memiliki masalah.


Sementara itu, untuk sebagian besar kasus penggunaan tr, Anda dapat menggunakan GNU sed atau GNU awk yang mendukung karakter multi-byte.

Misalnya, Anda:

tr -cs '[[:alpha:][:space:]]' ' '

dapat ditulis:

gsed -E 's/( |[^[:space:][:alpha:]])+/ /'

atau:

gawk -v RS='( |[^[:space:][:alpha:]])+' '{printf "%s", sep $0; sep=" "}'

Untuk mengonversi antara huruf kecil dan huruf besar ( tr '[:upper:]' '[:lower:]'):

gsed 's/[[:upper:]]/\l&/g'

(itu lhuruf kecil L, bukan 1digit).

atau:

gawk '{print tolower($0)}'

Untuk portabilitas, perladalah alternatif lain:

perl -Mopen=locale -pe 's/([^[:space:][:alpha:]]| )+/ /g'
perl -Mopen=locale -pe '$_=lc$_'

Jika Anda tahu data dapat direpresentasikan dalam set karakter byte tunggal, maka Anda dapat memprosesnya dalam rangkaian karakter itu:

(export LC_ALL=ru_RU.iso88595
 iconv -f utf-8 |
   tr -cs '[:alpha:][:space:]' ' ' |
   iconv -t utf-8) < Russian-file.utf8
Stéphane Chazelas
sumber
1
Saya telah menerima pertanyaan Anda karena informasi tentang tr. Saya telah memecahkan masalah, dan menghapus pertanyaan tentang bagaimana menyelesaikannya (jadi orang yang mencari tr hanya akan menemukan informasi tentang tr, bukan masalah sewenang-wenang). Jika Anda dapat menghapus solusi juga, karena tidak lagi diperlukan, saya akan berterima kasih.
MatthewRock
3
@MatthewRock Saya telah menyimpannya tetapi menulis ulang dan membuatnya lebih umum karena memberikan kata-kata di sekitar akan bermanfaat bagi orang-orang dengan masalah yang sama.
Stéphane Chazelas
Di mana Anda mendapatkan gagasan bahwa Cyrillic (biasanya) dikodekan dalam ISO 8859-5? Apakah Anda pernah melihat teks Rusia selain Unicode?
Incnis Mrsi
9
@IncnisMrsi, yang penting di sini adalah bahwa ISO 8859-5 adalah salah satu rangkaian karakter byte tunggal yang memiliki karakter Cyrillic tersebut. Apakah itu digunakan secara luas atau tidak tidak relevan di sini. Jika Anda memiliki lokal dengan KOI-R atau window-1251 charset, tentu saja, gunakan saja.
Stéphane Chazelas
@IncnisMrsi Bahasa Rusia di web hampir selalu dikodekan dalam UTF-8 (atau kadang-kadang pada Windows-1251), tetapi hanya karena kita telah merasakan sakitnya banyak pengkodean byte tunggal sejak awal. Berikut adalah halaman web kuno (sekitar 1998) dengan switcher pengkodean (non-fungsional): sch57.ru/collect .
Alex Shpilkin