Apakah (seharusnya) LC_COLLATE memengaruhi rentang karakter?

27

Urutan koleksi melalui LC_COLLATEmendefinisikan tidak hanya urutan urutan karakter individu, tetapi juga arti rentang karakter. Atau apakah itu? Pertimbangkan cuplikan berikut:

unset LANGUAGE LC_ALL
echo B | LC_COLLATE=en_US grep '[a-z]'

Secara intuitif, Btidak ada [a-z], jadi ini seharusnya tidak menghasilkan apa-apa. Itulah yang terjadi di Ubuntu 8.04 atau 10.04. Tetapi pada beberapa mesin yang menjalankan Debian lenny atau squeeze, Bditemukan, karena jangkauan a-zmencakup semua yang ada di antara adan zdalam urutan pemeriksaan, termasuk huruf kapital yang Bdilalui Z.

Semua sistem yang diuji memang memiliki en_USlokal yang dihasilkan. Saya juga mencoba memvariasikan lokal: pada mesin Byang cocok di atas, hal yang sama terjadi di setiap lokal yang tersedia (kebanyakan berbasis latin:, {en_{AU,CA,GB,IE,US},fr_FR,it_IT,es_ES,de_DE}{iso8859-1,iso8859-15,utf-8}juga lokal Cina) kecuali Jepang (dalam penyandian yang tersedia) dan C/ POSIX.

Apa arti rentang karakter dalam ekspresi reguler , ketika Anda melampaui ASCII? Mengapa ada perbedaan antara beberapa instalasi Debian di satu sisi, dan instalasi Debian lainnya dan Ubuntu di sisi lain? Bagaimana sistem lain berperilaku? Siapa yang benar, dan siapa yang seharusnya melaporkan bug?

(Perhatikan bahwa saya secara khusus bertanya tentang perilaku rentang karakter seperti [a-z]di en_USlokal, terutama pada sistem berbasis libc GNU. Saya tidak bertanya bagaimana mencocokkan huruf kecil atau huruf kecil ASCII.)


Pada dua mesin Debian, satu di mana Bada [a-z]dan satu di mana tidak, output dari LC_COLLATE=en_US locale -k LC_COLLATEadalah

collate-nrules=4
collate-rulesets=""
collate-symb-hash-sizemb=1
collate-codeset="ISO-8859-1"

dan output dari LC_COLLATE=en_US.utf8 locale -k LC_COLLATEis

collate-nrules=4
collate-rulesets=""
collate-symb-hash-sizemb=2039
collate-codeset="UTF-8"
Gilles 'SANGAT berhenti menjadi jahat'
sumber
1
Tidak mereproduksi pada contoh Debian Lenny saya sudah berguna. Namun tidak memeriksa apakah en_USdihasilkan.
alex
1
@ alex Jika lokal tidak dihasilkan, Clokal digunakan sebagai fallback, dan urutan susunannya adalah nilai byte langsung, jadi Btidak akan cocok. Tes di lokal yang muncul di output locale -a.
Gilles 'SO- berhenti bersikap jahat'
1
Perhatikan bahwa en_US TIDAK sama dengan en_US.utf8, dan biasanya berarti en_US.iso-8859-1, tergantung pada apa yang telah Anda instal. Jika en_US (tanpa akhiran) tidak muncul di output lokal -a Anda tidak benar-benar memiliki lokal ini. Apa yang ditampilkan LC_COLLATE = en_US lokal -k LC_COLLATE?
Neil Mayhew
1
Ini sejak itu muncul dalam pertanyaan praktis daripada teoritis di sini: Mengapa huruf kapital termasuk dalam berbagai huruf kecil dalam regeks awk?
Caleb
1
@isaac Sayangnya, 7 tahun kemudian, sepertinya saya tidak memiliki akses ke sistem yang bermasalah. Semuanya telah ditingkatkan atau dinonaktifkan.
Gilles 'SANGAT berhenti menjadi jahat'

Jawaban:

3

Jika Anda menggunakan sesuatu selain Clokal, Anda tidak boleh menggunakan rentang seperti [a-z]karena ini bergantung pada lokal dan tidak selalu memberikan hasil yang Anda harapkan. Seperti halnya kasus yang Anda temui, beberapa lokal memperlakukan karakter dengan diakritik (misalnya á ) sama dengan karakter dasar (yaitu a ).

Sebagai gantinya, gunakan kelas karakter bernama:

echo B | grep '[[:lower:]]'

Ini akan selalu memberikan hasil yang benar untuk lokal. Namun, Anda harus memilih lokal untuk mencerminkan arti dari teks input dan tes yang Anda coba terapkan.

Misalnya, jika Anda perlu menemukan nilai byte tertentu, gunakan Clokal, yang selalu tersedia:

echo B | LANG=C grep '[a-z]'

Jika ini tidak berfungsi seperti yang diharapkan, itu benar-benar bug.

Neil Mayhew
sumber
Saya tahu itu, bukan itu yang saya minta. Saya secara khusus bertanya tentang apa arti rentang eksplisit, dan mengapa distribusi yang berbeda (bahkan dengan GNU libc dan GNU grep) memiliki perilaku yang berbeda. (Diturunkan karena meskipun apa yang Anda katakan itu benar, itu tidak relevan.)
Gilles 'SO- stop being evil'
1
Maksud saya adalah bahwa arti dari rentang eksplisit bergantung pada lokal, dan sistem yang berbeda tidak diperlukan untuk mendefinisikan lokal mereka dengan cara yang sama, jadi ini bukan bug. Secara teknis, Anda menyalahgunakan sistem, jadi Anda seharusnya tidak terkejut mendapatkan perilaku "tidak terdefinisi". Juga, beberapa orang berkomentar bahwa mereka tidak dapat mereproduksi perilaku pada sistem Debian mereka, jadi sepertinya ada sesuatu yang tidak biasa pada sistem Anda.
Neil Mayhew
1
Saya tahu bahwa perilaku rentang tergantung pada lokasi. Saya bertanya bagaimana, dan terkejut bahwa sistem yang berbeda menggunakan Glibc (dan, ternyata, bahkan instalasi yang berbeda dari rilis Debian yang sama) memiliki perilaku yang berbeda. Saya telah menambahkan output locale -kke pertanyaan saya; itu identik pada dua mesin Debian, satu di mana Bberada dalam kisaran dan satu di mana tidak. BTW Saya tidak melakukan root pada salah satu mesin (jadi itu bukan sesuatu yang aneh yang saya lakukan sebagai admin).
Gilles 'SANGAT berhenti menjadi jahat'
echo "Baü" | LC_COLLATE=C grep -o '[[:lower:]]'mengembalikan aAND üsementara echo "Baü" | LC_COLLATE=C grep -o '[a-z]'hanya mengembalikan a. Di mata saya, "lebih rendah" sebenarnya bukan yang diinginkan OP
Daniel Alder
Meskipun begitu, poin asli saya masih berlaku: jangan gunakan rentang kecuali Anda berada di Clokal. Saya percaya ini relevan dengan OP, yang ingin melaporkan bug. Jika Anda tidak berada di Clokal, hasil menggunakan rentang sangat tidak terduga dan karenanya tidak pernah dapat dianggap sebagai bug. Di sisi lain, jika Anda perlu menemukan nilai byte tertentu, cukup gunakan Clokal. Poin kedua saya adalah jika Anda benar-benar ingin mencari huruf kecil di lokal, gunakan kelas karakter. Meskipun OP mungkin tidak mencari ini, orang lain mungkin jika mereka menemukan pertanyaan ini.
Neil Mayhew
1

Kisaran dalam ekspresi reguler harus memperhatikan pengaturan collation. Berikut adalah standar yang relevan: http://pubs.opengroup.org/onlinepubs/007908799/xbd/re.html (lihat "rentang ekspresi"). Jadi echo B | LC_COLLATE=en_US grep '[a-z]'seharusnya output Bdiberikan definisi yang masuk akal dari masing-masing lokal. Saya tidak bisa menjelaskan mengapa ini kadang-kadang tidak berhasil untuk Anda, tetapi saya akan sangat terkejut jika saya menemukan ini pada sistem non-kuno yang diinstal dan dikonfigurasi dengan benar.

Peter Eisentraut
sumber
1
echo B | LC_COLLATE=en_US.utf8 grep '[a-z]' Tidak mencetak apa pun di Ubuntu 12.04 dengan grep 2.10. Tidak mencetak apa pun di Centos 6.5 dengan grep 2.6.3. Bekerja pada Debian 6.0.8 dengan grep 2.6.3.
Ian D. Allen