Làm thế nào để làm cho tr nhận biết các ký tự không phải ascii (unicode)?


36

Tôi đang cố xóa một số ký tự khỏi tệp (UTF-8). Tôi đang sử dụng trcho mục đích này:

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

Tệp có chứa một số ký tự nước ngoài (như "тЛттииии" "" hoặc "àé"). trdường như không hiểu họ: nó coi họ là phi alpha và cũng loại bỏ.

Tôi đã thử thay đổi một số cài đặt ngôn ngữ của mình:

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

Thật không may, không ai trong số này làm việc.

Làm cách nào để trhiểu Unicode?

Câu trả lời:


29

Đó là một hạn chế đã biết ( 1 , 2 , 3 , 4 , 5 , 6 ) của việc triển khai GNU tr.

Nó không nhiều đến mức nó không hỗ trợ các ký tự nước ngoài , không phải tiếng Anh hoặc không phải ASCII, nhưng nó không hỗ trợ các ký tự nhiều byte.

Các ký tự Cyrillic đó sẽ được xử lý OK, nếu được viết trong bộ ký tự iso8859-5 (một byte cho mỗi ký tự) (và ngôn ngữ của bạn đang sử dụng bộ ký tự đó), nhưng vấn đề của bạn là bạn đang sử dụng UTF-8 trong đó không phải ASCII các ký tự được mã hóa từ 2 byte trở lên.

GNU có một kế hoạch (xem thêm ) để khắc phục điều đó và công việc đang được tiến hành nhưng chưa có.

FreeBSD hoặc Solaris trkhông có vấn đề.


Trong thời gian trung bình, đối với hầu hết các trường hợp sử dụng tr, bạn có thể sử dụng GNU sed hoặc GNU awk hỗ trợ các ký tự nhiều byte.

Chẳng hạn, của bạn:

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

có thể được viết:

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

hoặc là:

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

Để chuyển đổi giữa chữ thường và chữ hoa ( tr '[:upper:]' '[:lower:]'):

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

(đó llà chữ thường L, không phải 1chữ số).

hoặc là:

gawk '{print tolower($0)}'

Đối với tính di động, perllà một thay thế khác:

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

Nếu bạn biết dữ liệu có thể được biểu diễn trong một bộ ký tự một byte, thì bạn có thể xử lý nó trong bộ ký tự đó:

(export LC_ALL=ru_RU.iso88595
 iconv -f utf-8 |
   tr -cs '[:alpha:][:space:]' ' ' |
   iconv -t utf-8) < Russian-file.utf8

1
Tôi đã chấp nhận câu hỏi của bạn vì thông tin về tr. Tôi đã giải quyết vấn đề và xóa câu hỏi về cách giải quyết vấn đề này (vì vậy những người tìm kiếm tr sẽ chỉ tìm thấy thông tin về tr chứ không phải một số vấn đề tùy tiện). Nếu bạn có thể vui lòng xóa giải pháp quá, vì nó không còn cần thiết nữa, tôi rất biết ơn.
MatthewRock

3
@MatthewRock Tôi đã giữ nó nhưng sắp xếp lại và nói chung chung hơn vì đưa ra một từ xung quanh sẽ hữu ích cho những người có cùng vấn đề.
Stéphane Chazelas

Nơi nào bạn có ý tưởng rằng Cyrillic (thông thường) được mã hóa theo ISO 8859-5? Bạn đã bao giờ nhìn thấy một văn bản tiếng Nga trong bất cứ điều gì trừ Unicode?
Incni Mrsi

9
@IncnisMrsi, tất cả những gì quan trọng ở đây là ISO 8859-5 là một trong những bộ ký tự đơn có các ký tự Cyrillic đó. Cho dù nó được sử dụng rộng rãi hay không thì không liên quan ở đây. Nếu bạn có một miền địa phương với bộ ký tự KOI-R hoặc window-1251, bằng mọi cách, hãy sử dụng nó để thay thế.
Stéphane Chazelas

@IncnisMrsi Tiếng Nga trên web hầu như luôn được mã hóa bằng UTF-8 (hoặc đôi khi trong Windows-1251), nhưng chỉ vì chúng tôi cảm thấy đau đớn vì nhiều mã hóa một byte sớm. Đây là một trang web cổ (khoảng năm 1998) với trình chuyển đổi mã hóa (không chức năng): sch57.ru/collect .
Alex Shpilkin
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.