ไม่สามารถใช้ `cut -c` (` --characters`) กับ UTF-8 ได้หรือไม่?


15

คำสั่งcutมีตัวเลือกในการทำงานกับตัวละครแทนของไบต์ที่มีตัวเลือก-c -bแต่ดูเหมือนจะไม่ทำงานในen_US.UTF-8สถานที่:

ไบต์ที่สองให้อักขระ ASCII ตัวที่สอง (ซึ่งเข้ารหัสเหมือนกันใน UTF-8):

$ printf 'ABC' | cut -b 2          
B

แต่ไม่ได้ให้อักขระที่สองในสามของอักขระที่ไม่ใช่ ASCII กรีกในโลแคล UTF-8:

$ printf 'αβγ' | cut -b 2         
�

ว่าไม่เป็นไร - เป็นที่สองไบต์
ดังนั้นเรามองไปที่ตัวละครที่สองแทน:

$ printf 'αβγ' | cut -c 2 
�

ดูหัก
ด้วยการทดลองบางอย่างปรากฎว่าช่วง3-4แสดงตัวละครที่สอง:

$ printf 'αβγ' | cut -c 3-4
β

แต่นั่นก็เหมือนกับไบต์ 3 ถึง 4:

$ printf 'αβγ' | cut -b 3-4
β

ดังนั้น-cไม่เกิน-bUTF-8

ฉันคาดว่าการตั้งค่าภาษาจะไม่เหมาะสมสำหรับ UTF-8 แต่ในการเปรียบเทียบwcทำงานได้ตามที่คาดไว้
มันมักจะใช้ในการนับไบต์ด้วยตัวเลือก-c( --bytes) (สังเกตชื่อตัวเลือกที่สับสน)

$ printf 'αβγ' | wc -c
6

แต่ก็สามารถนับตัวอักษรด้วยตัวเลือก-m( --chars) ซึ่งใช้งานได้:

$ printf 'αβγ' | wc -m
3

ดังนั้นการกำหนดค่าของฉันดูเหมือนว่าจะเป็น OK - cutแต่สิ่งที่เป็นเรื่องเกี่ยวกับพิเศษ

อาจจะไม่รองรับ UTF-8 เลยเหรอ? แต่ก็ไม่ดูเหมือนจะสนับสนุนอักขระหลายไบต์มิฉะนั้นก็จะไม่จำเป็นต้องให้การสนับสนุนและ-b-c

แล้วมีอะไรผิดปกติ? และทำไม?


การตั้งค่าโลแคลนั้นเหมาะสมสำหรับ utf8 เท่าที่ฉันจะบอกได้:

$ locale
LANG=en_US.UTF-8
LANGUAGE=en_US
LC_CTYPE=en_US.UTF-8
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

อินพุตไบต์ต่อไบต์:

$ printf 'αβγ' | hd 
00000000  ce b1 ce b2 ce b3                                 |......|
00000006

! ที่น่าสนใจ ดูเหมือนว่าจะใช้รหัสเดียวกันเป็น-c -bคุณได้ดูซอร์สโค้ดหรือไม่? บางทีคุณอาจพบคำใบ้ว่าสิ่งที่-cมีความหมายสำหรับ
michas

คำตอบ:


13

คุณยังไม่ได้บอกว่าcutคุณใช้อะไร แต่เนื่องจากคุณพูดถึงตัวเลือกแบบยาวของ GNU --charactersฉันจะถือว่ามันเป็นตัวเลือกที่ดี ในกรณีนั้นให้สังเกตข้อความนี้จากinfo coreutils 'cut invocation' :

‘-c character-list’
‘--characters=character-list’

เลือกเพื่อพิมพ์เฉพาะอักขระในตำแหน่งที่ระบุไว้ในรายการตัวอักษร เช่นเดียวกับ-bตอนนี้แต่ความเป็นสากลจะเปลี่ยนไป

(เน้นเพิ่ม)

ในขณะนี้ GNU cutจะทำงานในรูปแบบของ "อักขระ" ไบต์เดียวเสมอดังนั้นพฤติกรรมที่คุณเห็นเป็นที่คาดหวัง


การสนับสนุนทั้งPOSIX-bและออปชั่น-cเป็นสิ่งจำเป็นโดย POSIX - ไม่ได้เพิ่มใน GNU cutเนื่องจากมีการสนับสนุนหลายไบต์และทำงานอย่างถูกต้อง แต่เพื่อหลีกเลี่ยงข้อผิดพลาดในการป้อนข้อมูลที่สอดคล้องกับ POSIX เช่นเดียวกัน-cนี้เกิดขึ้นในcutการใช้งานบางอย่างแม้ว่าจะไม่ใช่FreeBSDและOS Xอย่างน้อยก็ตาม

นี่คือพฤติกรรมที่มีคุณค่าทางประวัติศาสตร์-cของ -bถูกเพิ่มเข้ามาใหม่เพื่อรับบทบาทไบต์เพื่อให้-cสามารถทำงานกับอักขระหลายไบต์ บางทีในไม่กี่ปีมันจะทำงานได้ตามที่ต้องการอย่างต่อเนื่องแม้ว่าความคืบหน้าจะไม่เร็วอย่างแน่นอน (มันผ่านมานานกว่าทศวรรษ) GNU cut ยังไม่ได้ใช้-nตัวเลือกแม้ว่าจะเป็นฉากฉากและมีจุดประสงค์เพื่อช่วยให้เกิดการเปลี่ยนแปลง มีปัญหาความเข้ากันได้ที่อาจเกิดขึ้นกับสคริปต์เก่าซึ่งอาจเป็นข้อกังวลถึงแม้ว่าฉันจะไม่ทราบสาเหตุที่แน่ชัด


1
การทำงานที่ดี. คุณจะพบความคิดเห็นประเภทเดียวกันในtrเอกสารของ GNU เช่นกัน และtarถ้าฉันไม่ได้รับการยกเว้น ฉันคิดว่ามันเป็นโครงการใหญ่
mikeserv

มีวิธีแก้ไขปัญหาใด ๆ สำหรับ Unicode probelm cutหรือไม่? ยกตัวอย่างเช่นที่มันเป็นไปได้ที่จะดาวน์โหลดแหล่งที่มาสำหรับปะcut? หรือว่าจะใช้ยูทิลิตี้อื่นได้ง่ายขึ้นหรือไม่ ( grepโซลูชันด้านล่างไม่ทำงานอย่างราบรื่นกับช่วงเช่น5-8,44-49)
dma_k

ดูบทความ 2017 หัวข้อย่อย” บันทึกย่อและตัวชี้แบบสุ่มเกี่ยวกับความพยายามอย่างต่อเนื่องในการเพิ่มการสนับสนุนหลายไบต์และ unicode ใน GNU Coreutils“ : crashcourse.housegordon.org/coreutils-multibyte-support.html
myrdd

คุณสามารถหาทางเลือกอื่นได้cut -cที่นี่: superuser.com/questions/506164/…
myrdd

5

colrm(ส่วนหนึ่งของutil-linuxควรได้รับการติดตั้งแล้วในการกระจายส่วนใหญ่) ดูเหมือนว่าจะจัดการกับความเป็นสากลได้ดีขึ้นมาก:

$ echo 'αβγ' | colrm 3
αβ
$ echo 'αβγ' | colrm 2
α

ระวังของหมายเลข: colrm Nจะลบคอลัมน์จากการพิมพ์ตัวอักษรขึ้นไปNN-1

( เครดิต )


2

เนื่องจากgrepการใช้งานที่มีสัญลักษณ์ที่ทราบคุณยังสามารถใช้ในการจำลองการใช้งานบางส่วนgrep -ocut -c

$ echo Τηεοδ29 | grep -o '^..'
Τη
$ echo Τηεοδ29 | egrep -o '^..' | grep -o '.$'
η

ปรับจำนวนงวดเพื่อจำลองcutช่วง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.