ใน bash ฉันจะแปลง Unicode Codepoint [0-9A-F] เป็นอักขระที่พิมพ์ได้ได้อย่างไร


23

ฉันมีรายการ codepoints ของ Unicode แต่ฉันไม่รู้วิธี "ง่าย" ในการแปลงค่าเลขฐานสิบหกเหล่านี้ให้เป็นอักขระจริงที่พวกเขาเป็นตัวแทน ...

ฉันได้ยินว่าzshมีecho -e '\u0965'แต่ฉันใช้bash 4.1

มีบางสิ่งที่ง่ายเหมือนวิธี zsh สำหรับทุบตี?


คำตอบ:


16

คุณสามารถใช้ echo ของ bash หรือ / bin / echo จาก GNU coreutils ร่วมกับ iconv:

echo -ne '\x09\x65' | iconv -f utf-16be

ตามค่าเริ่มต้น iconv จะแปลงเป็นการเข้ารหัสของโลแคล บางทีแบบพกพามากกว่าอาศัยเชลล์หรือคำสั่ง echo เฉพาะคือ Perl ระบบ UNIX ส่วนใหญ่ที่ฉันรู้จักในขณะที่มี Perl พร้อมใช้งานและยังมีพอร์ต Windows หลายพอร์ต

perl -C -e 'print chr 0x0965'

ส่วนใหญ่เวลาที่ฉันต้องทำสิ่งนี้ฉันอยู่ในเอดิเตอร์เช่น Vim / GVim ซึ่งมีการสนับสนุนในตัว ขณะอยู่ในโหมดแทรกให้กด Ctrl-V แล้วตามด้วย u จากนั้นพิมพ์อักขระฐานสิบหกสี่ตัว หากคุณต้องการตัวอักษรที่เกิน U + FFFF ให้ใช้ตัวพิมพ์ใหญ่ U และพิมพ์ตัวอักษรฐานสิบหก Vim ยังสนับสนุนการสร้างคีย์แมปแบบกำหนดเองได้อย่างง่ายดาย มันจะแปลงชุดอักขระเป็นสัญลักษณ์อื่น ตัวอย่างเช่นฉันมี keymap ที่ฉันพัฒนาขึ้นเรียกว่า www มันจะแปลง TM เป็น™, (C) เป็น©, (R) ถึง®และอื่น ๆ ฉันยังมี keymap สำหรับ Klingon สำหรับเมื่อจำเป็น ฉันแน่ใจว่า Emac มีบางอย่างที่คล้ายกัน หากคุณอยู่ในแอพ GTK + ซึ่งรวมถึง GVim และเทอร์มินัล GNOME คุณสามารถลอง Control-Shift-u ตามด้วยอักขระฐานสิบหก 4 ตัวเพื่อสร้างอักขระ Unicode ฉันแน่ใจว่า KDE / Qt มีบางอย่างที่คล้ายกัน

UPDATE:ในฐานะของ Bash 4.2 ดูเหมือนว่าจะมีฟีเจอร์ในตัวในขณะนี้:

echo $'\u0965'

ปรับปรุง:นอกจากนี้ในปัจจุบันตัวอย่างงูหลามอาจจะต้องการ Perl ใช้ได้ทั้ง Python 2 และ 3:

python -c 'print(u"\u0965")'

ขอบคุณ ... perl หนึ่งในดีและสั้น แต่มันทำให้ฉันงุนงงเป็นวิธีที่รู้วิธีการรักษาค่าเป็น UTF-16BE .. ฉันเดาว่านั่นคือสิ่งที่ "chr" หมายถึง ...
Peter.O

@ เฟรดนั่นเป็นจุดที่ดี ตัวอย่าง Perl เป็นสถานที่สำคัญ -C เปิดใช้งานการประมวลผล Unicode แบบเต็ม แต่ตัวอย่างใช้งานได้เนื่องจากภาษาของฉันใช้ตัวอย่าง Unicode ถ้าฉันตั้งค่า LANG เป็น C ฉันจะได้รับคำเตือนเกี่ยวกับตัวละครที่กว้างในการพิมพ์ แต่ก็ยังคงพิมพ์ ถ้าฉันพิมพ์chr 0xa2ในภาษา UTF-8 ฉันจะได้รับเซ็นต์เซ็นต์¢ แต่ถ้าฉันใช้ LANG = C ฉันจะได้ get เพราะมันพิมพ์ไบต์ 0xa2 ซึ่งไม่ถูกต้องใน UTF-8 ตัวอย่าง Vim / GVim มีความไวต่อกึ่งโลแคล การเข้ารหัสไฟล์ที่ถูกต้องมากขึ้น หากคุณเริ่มต้นเป็นกลุ่มในสถานที่ที่ไม่ใช่ UTF-8 คุณจะต้อง:set encoding=utf-8
penguin359

@ เฟรดฉันควรชี้ให้เห็นว่า Perl รักษาคุณค่าของ chr เป็น Unicode codepoint ถ้า Perl เริ่มต้นในโลแคล Unicode เช่น UTF-8 codepoint เป็นหมายเลขเฉพาะที่แสดงถึงตัวละครและไม่เชื่อมโยงกับการเข้ารหัสใด ๆ เช่น UTF-16BE หรือ UTF-8 มันจะแปลงเป็นการเข้ารหัสที่ถูกต้องเมื่อพิมพ์ออกมา ตัวอย่างเช่น Cuneiform Sign A คือ codepoint U + 012000 ฉันสามารถใช้chr 0x12000ใน Perl (สมมติว่า Unicode ใช้งานได้) เพื่อแสดงมัน ใน UTF-16BE นี่คือ 0xd8, 0x08, 0xdc และ 0x00 อักขระของคุณคือ U + 0965 ซึ่งเพิ่งเกิดขึ้นเป็นไบต์ 0x09 ตามด้วย 0x65 ใน UTF-16BE
penguin359

@ penguin359 .. ขอบคุณวันหนึ่ง (หวังว่า) ฉันจะได้ดู perl .. ดูเหมือนว่าจะเป็นความลับที่ไม่อาจหยั่งรู้ได้ แต่จากนั้นก็ทำให้ sed และ regex เริ่มแรกและตอนนี้มันค่อนข้างง่าย ... อาจจะเล็กน้อย ชอบเป็นกลุ่ม; เส้นโค้งการเรียนรู้ที่สูงชันจากนั้นแล่นเรือใบธรรมดา .... มันเป็นเรื่องดีที่จะอ่านคำอธิบายของคุณ ...
มันปู

ฉันเพิ่งค้นพบอีกว่างานพิมพ์ของSteven Dจะไม่จัดการบล็อก ASCII ของช่วง Unicode ดังนั้นperlคำตอบของคุณคือสิ่งที่ดีที่สุด (สำหรับข้อกำหนดเฉพาะของฉัน) .. ก่อนหน้านี้ฉันได้ตัดการพิมพ์ printf (เดือนก่อน) แต่ฉันลืมไปแล้ว นี่คือภารกิจ / คำตอบเกี่ยวกับข้อ จำกัด ของมัน ... เหตุใด printf จึงรายงานข้อผิดพลาดในทั้งหมดยกเว้น Unicode Codepoints (ASCII-range) สามอัน
Peter.O

13

ทุบตี 4.2 (ปล่อยตัวในปี 2011) เพิ่มการสนับสนุนสำหรับecho -e '\u0965', printf '\u0965', printf %b '\u0965'และecho $'\u0965'ยังทำงาน

http://tiswww.case.edu/php/chet/bash/FAQ :

o   $'...', echo, and printf understand \uXXXX and \UXXXXXXXX escape sequences.

ขอบคุณ ... ฉันยังคงใช้ bash 4.1.5 เป็นหลักใน Ubuntu 10.04 แต่ก็เป็นเรื่องดีที่ได้รู้ว่าตอนนี้มีให้ใช้ใน 4.2 (+1)
Peter.O

1
+1; ทราบว่าbash 4.2.xรุ่นที่มีข้อผิดพลาดที่ค่าระหว่าง0x80และ0xff( 128 - 255) - คืออยู่ในช่วงขยาย ASCII - จะไม่ถูกต้อง UTF8 เข้ารหัสและแทนที่จะเป็นเพียงแค่ผ่านส่งผลให้ถ่าน UTF8 ?ไม่ถูกต้องที่ขั้วบางทำให้เป็น ณ (อย่างน้อย) 4.3.11สิ่งนี้ได้รับการแก้ไขแล้ว ถ้าecho $'\ued'แสดงผลíบั๊กจะไม่มีอยู่
mklement0

5

หากคุณมี coreutils GNU ลองprintf:

$ printf '\u0965\n'

echo สามารถทำงานได้ถ้าคอนโซลของคุณใช้ UTF-8 และคุณมีการเข้ารหัส UTF-8:

$ echo -e '\xE0\xA5\xA5'

คุณสามารถค้นหาตาราง Unicode เพื่อการเข้ารหัส UTF-8 ฐานสิบหกที่นี่: http://www.utf8-chartable.de/ คุณสามารถแปลงคะแนนโค้ด Unicode ให้เป็นเลขฐานสิบหกโดยใช้ภาษาสคริปต์จำนวนหนึ่ง นี่คือตัวอย่างการใช้งานไพ ธ อน:

python -c "print(unichr(int('0965', 16)).encode('utf-8').encode('hex'))"

ต่อไปนี้เป็นสคริปต์ Perl ที่จะแปลงอาร์กิวเมนต์เป็นค่าฐานสิบหกที่ถูกต้อง (วงเล็บที่ไม่จำเป็นจำนวนมากที่นี่):

#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Encode;

foreach (@ARGV) {
    say unpack('H*', encode('utf8', chr(hex($_))))
}

ตัวอย่างเช่น

./uni2utf 0965
e0a5a5

แน่นอนว่าถ้าคุณมี Perl หรือ Python คุณก็สามารถใช้มันเพื่อพิมพ์อักขระได้


ขอบคุณ .. echoสิ่งที่ฉันไม่ต้องการเพราะ Codepoints นั้นเป็น 2-byte UTF-16 Big-Endian .. แต่คุณได้เตือนฉันว่ามี2หน้าที่ printf! (ฉันคิดว่า printf สามารถทำได้และดูเหมือนว่าฉันกำลังเรียกผิด) ... ใช้$(which printf)งานได้ ... ขอบคุณสำหรับตัวอย่างของไพ ธ อน .. แต่สำหรับเรื่องนี้ (เส้นโค้งการเรียนรู้ของฉัน) ฉันพยายามที่จะปิด เป็นไปได้ที่จะ "ทุบตี" เป็นภาษาที่แสกหน้าเท่านั้นที่เกี่ยวข้อง .. (เมื่อฉันสบายพอที่จะทุบตีฉันจะติดอยู่กับงูหลาม ... btw .encode('hex')เป็นขั้นตอนเดียวที่เกินกว่าที่ฉันต้องการ .. (ฉันคิดว่ามันดู บิตว่างในการมี :)
Peter.O

ใช่. encode ('hex') เป็นเพียงการรับรหัสฐานสิบหกที่ดูเหมือนว่าจะทำงานกับ echo สำหรับฉัน ดีใจที่อย่างน้อยส่วนนี้มีประโยชน์
Steven D

ฉันเพิ่งเห็นคุณตัวอย่างข้อมูล .. ขอบคุณ ... มันเป็นเรื่องดีที่จะมีวิธีแก้ปัญหาต่าง ๆ เหล่านี้ ... คนหนึ่ง printf คือสิ่งที่ฉันกำลังมองหา (คำสั่งเดียวตามตัวอย่าง zsh) ... .. ฉันอาจจะดีโพสต์ของฉันที่ไม่ใช้-อีกสคริปต์ภาษาวิธีการที่ทำงานในกระแสของข้อมูลฐานสิบหก (ไม่ \ U, ฯลฯ ) ..
Peter.O

ฉันชอบความกะทัดรัดของprintfข้างต้นโดยเฉพาะ แต่มันไม่ได้จัดการกับค่าที่ต่ำกว่า `` \ u00A0 ... I've just re-discovered something I already knew (but dropped off the radar)... Here is a Question I asked about 4 months ago; [Why does printf report an error on all but three (ASCII-range) Unicode Codepoints](http://askubuntu.com/questions/20806/why-does-printf-report-an-error-on-all-but-three-ascii-range-unicode-codepoints)... So *penguin359's* perl` ทางออกดูดีมากตอนนี้ :) .. มันเป็น invocaton เดียวและฉันหลังจาก "พิมพ์ง่าย" ดังนั้นฉันจะให้ เห็บสีเขียวสำหรับเขาperl
Peter.O

2

อัปเดต: นี่คือวิธีทุบตีในการทำค่า Unicode เดียว ... (โดย "ทุบตี" ฉันหมายถึง: ไม่ได้ใช้ภาษาสคริปต์อื่น ๆ ) .. ขอบคุณ Gilles สำหรับคำแนะนำในAskubuntu Q / Aนี้
ตามลิงค์นี้ : recode (Obsoletes iconv, dos2unix, unix2dos) .. แก้ไข:แต่ตามความคิดเห็นด้านล่าง "obsoletes 'อาจหมายถึง" ทางเลือก "

      echo -n 0x0965 |recode UTF-16BE/x4..UTF-8

นี่คือวิธีการประมวลผลการถ่ายโอนข้อมูล hex ดิบเป็นอินพุต (เช่นไม่มีคำนำหน้าหนีเช่น; \ u0965 และไม่มี \ x09 \ x65) ..
xxdเป็นยูทิลิตี้ hex-dump (บรรจุด้วยvim-common) ซึ่งสามารถเปลี่ยนการถ่ายโอนข้อมูลดิบ hex สำหรับอักขระที่ดัมพ์แทน ... Unicode Codepoints คือ UTF-16BigEndian ซึ่งเป็นสิ่งที่ Hex-dump คือ ..
xxdในโหมดย้อนกลับยอมรับกระแสของค่า Hex ที่มีการแบ่งบรรทัดซึ่งจะถูกละเว้น

สคริปต์นี้สร้างสตรีม UTF-16BE ซึ่งจะเปลี่ยนเป็นตัวอักษรดั้งเดิม
บรรทัดสุดท้ายมีสองคำสั่งที่จำเป็น xxdและiconv

for line in \
  "Matsuo Basho (1644-1694)" \
  "  pond" \
  "  frog jumps in" \
  "  plop!"
do 
  echo "$line" |iconv -f "$(locale charmap)" -t "UTF-16BE" |xxd -ps -u 
done |
#    (---this is the **revert** code---) 
tee >(xxd -p -u -r |iconv -f "UTF-16BE") ;echo

นี่คือผลลัพธ์ (แสดงอินพุต hex-dump UTF-16BE ก่อน)
บันทึก; xxdแบ่งกลุ่มเอาท์พุทของตัวเองด้วยการขึ้นบรรทัดใหม่ด้วยตัวเลขฐานสิบหก 60 ตัว ... ตัวเลือกการย้อนกลับจะละเว้นบรรทัดใหม่เหล่านี้ .. มันจะละเว้นการขึ้นบรรทัดใหม่ใด ๆ / ทั้งหมด

004D0061007400730075006F00200042006100730068006F002000280031
003600340034002D00310036003900340029000A
002000200070006F006E0064000A
0020002000660072006F00670020006A0075006D0070007300200069006E
000A
002000200070006C006F00700021000A

Matsuo Basho (1644-1694)
  pond
  frog jumps in
  plop!

เนื่องจากดูเหมือนว่าคุณใช้ข้อมูลของ penguin359 ในคำตอบของคุณคุณอาจลองทำเครื่องหมายคำตอบของเขาว่าถูกต้องแทนที่จะเป็นของฉัน
Steven D

@ สตีเว่นดี: ความเห็นที่น่าสังเกต แต่ "ดู" เป็นคำที่ใช้งาน ฉันใช้ iconv แบบนี้มาสองสามวันแล้วซึ่งทำให้ฉันสงสัยว่ามีคำสั่งเดียวไหม ฉันได้ทำการประมวลผลทั้งไฟล์ที่คล้ายกันใน windows (C ++) ดังนั้นฉันจึงมีความเข้าใจเหตุผลของ Unicode ฉันถูกจริงๆหลังจากที่รวดเร็วและง่ายbashวิธี โดย "bash" ฉันหมายถึง: ใช้ภาษาสคริปต์ทุบตี; ไม่ใช่ python / perl จากภายใน bash) ฉันได้เพิ่มสิ่งนี้เป็นคำตอบเพราะอาจมีค่าสำหรับบางคนที่อ่านหน้านี้ เป็นไฟล์เดียวที่ดีสำหรับไฟล์ทั้งหมด คุณprintfคือคำตอบที่ดีที่สุดสำหรับฉัน
Peter.O

2
ฉันจะไม่พูดว่า recode obsoletes iconv ในความเป็นจริงแล้ว recode นั้นมีอายุมากกว่า iconv และ iconv ในปัจจุบันมักติดตั้งโดยค่าเริ่มต้นมากกว่า recode (ตัวอย่างเช่นบน Linux, iconv ติดตั้งอยู่เสมอเพราะมาพร้อมกับ libc)
Gilles 'หยุดความชั่วร้าย' ใน

ขอบคุณ .. ฉันสงสัยเกี่ยวกับเรื่องนั้น .. หน้าเว็บนั้นไม่ใช่ข้อมูลอ้างอิงที่ชัดเจนแน่นอน ... ดังนั้นจึงเป็นอีกทางเลือกหนึ่ง ...
Peter.O

1

สมมติว่าการเข้ารหัสเริ่มต้นสำหรับระบบปฏิบัติการของคุณคือ UTF-8 (จริงสำหรับ distros ปัจจุบัน) จากนั้นคุณสามารถใช้ bash โดยตรงเพื่อแปลงจุดโค้ด UNICODE ใด ๆ :

echo -e "Unicode Character 'DEVANAGARI DOUBLE DANDA' (U+0965) \U0965"

แน่นอนสัญลักษณ์จะปรากฏขึ้นอย่างถูกต้องเฉพาะในกรณีที่คุณมีแบบอักษรที่ถูกต้อง ในฐานะของทุบตี 4.3 คะแนนรหัสทั้งหมดจะทำงานอย่างถูกต้อง และตัวเลือกสองตัวในตัวนี้ก็จะทำงาน:

printf "%b" "Unicode Character (U+0965) \U0965 \n"
echo $'Unicode Character (U+0965) \U0965'

โปรดทราบว่าสำหรับ bash 4.2 โค้ด Unicode 0x80จะ0xFFถูกเข้ารหัสอย่างไม่ถูกต้อง (bash bug) หากต้องการแก้ไขปัญหานี้คุณต้องดูที่โปรแกรมในไซต์นี้ (ซึ่งจะช่วยให้มองลึกลงไปถึงปัญหาของการแปลงตัวเลขเป็นตัวอักษร


ใช้งานได้สำหรับฉันในทุบตี 4.3 และ zsh มีรายงานข้อผิดพลาดสำหรับ bash 4.2 ที่คุณสามารถลิงก์ไปได้หรือไม่?
มิเคล

ดูเหมือนว่าฉันจะชอบข้อผิดพลาดที่ถูกต้อง: https://lists.gnu.org/archive/html/bug-bash/2012-02/msg00035.htmlคำอธิบาย: \ u และ \ U เข้ารหัสค่าระหว่าง \ u80 และ \ uff ไม่ถูกต้อง

0

ใช้รูปแบบการทดแทนในทุบตีรุ่น 4.2 (และสูงกว่า):

${parameter/pattern/string}

ตามที่อธิบายไว้ที่นี่http://steve-parker.org/sh/tips/pattern-substitution/

UNICODE_HEX="U+02211"
printf ${UNICODE_HEX/U+/"\U"}


UNICODE_HEX="U+03BB"
printf ${UNICODE_HEX/U+/"\U"}
λ         

1
ทราบว่าตามที่ระบุไว้ในคำตอบที่ก่อนหน้านี้ ,นี้จะทำงานเฉพาะในทุบตีเวอร์ชัน 4.2 (และสูงกว่า) ในความเป็นจริงนี่เป็นการเพิ่มคำตอบก่อนหน้านี้เล็กน้อย
G-Man กล่าวว่า 'Reinstate Monica'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.