ฉันจะแปลงตัวเลขเปอร์เซียเป็น UTF-8 เป็นตัวเลขยุโรปใน ASCII ได้อย่างไร


16

ในตัวเลขเปอร์เซีย۰۱۲۳۴۵۶۷۸۹เท่ากับ0123456789ในหลักยุโรป

ฉันจะแปลงหมายเลขเปอร์เซีย (เป็นUTF-8) เป็น ASCII ได้อย่างไร

ตัวอย่างเช่นผมต้องการที่จะกลายเป็น۲۱21


1
น่าสนใจดูเหมือนว่าecho "۰۱۲۳۴۵۶۷۸۹" | iconv -f UTF-8 -t ascii//TRANSLITจะไม่ได้จัดการ ...
Kusalananda

@Kusalananda ไม่ทำงาน
بارپابابا

3
@ Kusalananda: มันคาดไม่ถึงจริงๆเหรอ? ตามที่ฉันเข้าใจมันiconvเป็นเพียงที่นี่เพื่อจับคู่ตัวละครในการเข้ารหัสที่แตกต่างกัน แต่นี่คือตัวละคร (เลขอารบิคตะวันออก) ที่ไม่เทียบเท่าใน ASCII คุณสามารถแปลงให้เป็นสิ่งที่คล้ายกันมากพอ แต่เป็นแบบทางเดียวเท่านั้น
phk

3
ฉันไม่แน่ใจว่าสิ่งที่iconvมีความสามารถและไม่สามารถทำได้ ฉันหวังว่าการใช้งาน//TRANSLITจะช่วยได้ แต่ก็ไม่เป็นเช่นนั้น
Kusalananda

1
คุณต้องการกลับรายการสั่งซื้อด้วยหรือไม่ ฉันรู้ว่าเลขอารบิกเขียนจากขวาไปซ้ายเล็กน้อยและละตินเป็นตัวเลขใหญ่จากซ้ายไปขวา (ดูคล้ายกันในการพิมพ์หรือบนหน้าจอ แต่กลับในหน่วยความจำ) เปอร์เซียเหมือนกันหรือไม่
Toby Speight

คำตอบ:


6

เราสามารถใช้ประโยชน์จากความจริงที่ว่าจุดรหัส UNICODE ของตัวเลขเปอร์เซียนั้นต่อเนื่องกันและสั่งจาก 0 ถึง 9 :

$ printf '%b' '\U06F'{0..9}
۰۱۲۳۴۵۶۷۸۹

นั่นหมายความว่าเลขฐานสิบหกสุดท้ายคือค่าทศนิยม:

$ echo $(( $(printf '%d' "'۲") & 0xF ))
2

นั่นทำให้ลูปง่าย ๆ เป็นเครื่องมือแปลง:

#!/bin/bash
(   ### Use a locale that use UTF-8 to make the script more reliable.
    ### Maybe something like LC_ALL=fa_IR.UTF-8 for you?.
    LC_ALL=en_US.UTF-8
    a="$1"
    while (( ${#a} > 0 )); do
        # extract the last hex digit from the UNICODE code point
        # of the first character in the string "$a":
        printf '%d' $(( $(printf '%d' "'$a") & 15 ))
        a=${a#?}    ## Remove one character from $a
    done
)
echo

ใช้เป็น:

$ sefr.sh ۰۱۲۳۴۵۶۷۸۹
0123456789

$ sefr.sh ۲۰۱
201

$ sefr.sh ۲۱
21

โปรดทราบว่ารหัสนี้สามารถแปลงตัวเลขอารบิกและละติน (แม้ว่าจะผสม):

$ sefr.sh ۴4٤۵5٥۶6٦۷7٧۸8٨۹9٩
444555666777888999

$ sefr.sh ٤٧0٠٦7١٣3٥۶٦۷
4700671335667

ขอบคุณมากนี่เป็นคำตอบที่ดีมากและฉันมีคำถาม ,, ในคำสั่งนี้ให้พิมพ์ '% d' '"۰' ทำไมต้องใช้การเสนอราคาสองครั้ง?
بارپابابا

@Babyy มันไม่ได้เป็นคำพูดมันเป็นวิธีการที่จะให้ printf การโต้เถียงที่เริ่มต้นด้วยคำพูดเดียว: มันอาจถูกเขียนด้วยเช่น'"۰'กัน เหตุผลก็คือว่า printf จะให้จุดรหัส UNICODE ถ้าอาร์กิวเมนต์เริ่มต้นด้วยคำพูดเดียว'หรือคู่ "quote ค้นหาข้อความก่อนลิงค์นี้เพื่อหาข้อความ "ถ้าตัวละครนำคือคำพูดเดี่ยวหรือเครื่องหมายคำพูดคู่"

@Babyy รหัสได้รับการขยายการแปลงเปอร์เซีย, อาหรับและละติน (แม้ว่าจะผสม)

27

เนื่องจากเป็นชุดตัวเลขคงที่คุณสามารถทำได้ด้วยตนเอง:

$ echo ۲۱ | LC_ALL=en_US.UTF-8 sed -e 'y/۰۱۲۳۴۵۶۷۸۹/0123456789/'
21

(หรือใช้trแต่ยังไม่ใช่ GNU tr )

การตั้งค่าโลแคลของคุณเป็นen_US.utf8(หรือดีกว่าไปยังโลแคลที่จำเป็นต้องใช้ชุดอักขระ) sedเพื่อจดจำชุดอักขระของคุณ

ด้วยperl:

$ echo "۲۱" |
  perl -CS -MUnicode::UCD=num -MUnicode::Normalize -lne 'print num(NFKD($_))'
21

การตั้งค่าLC_ALLจำเป็นต้องมีเพื่อให้ทุกตัวอักษรยูนิโค้ดเดียวจะได้รับการพิจารณาด้วยเช่นsedกันใช่ไหม?
phk

@phk: ใช่เห็นการปรับปรุง
cuonglm

ทำไมทุกอย่างต้องเป็นบทที่เย่อหยิ่ง? เราไม่ได้ประดิษฐ์trเพื่อจุดประสงค์ที่แน่นอนนี้หรือไม่?
Kevin

3
@Kevin ดูคำตอบอื่น ๆ ที่เกี่ยวข้องกับtrวิธีที่มันไม่ทำงานทุก โปรดทราบว่าเครื่องมือบางอย่างได้รับการปรับให้เหมาะสมสำหรับการจัดการกับไบต์ในขณะที่เครื่องมืออื่น ๆ สำหรับจัดการกับอักขระด้วย Unicode (โดยเฉพาะ UTF-8) สิ่งนี้สร้างความแตกต่างอย่างมาก
phk

สิ่งนี้ใช้ไม่ได้กับฉันใน OS X 10.10.5 / GNU bash 4.3 วิจิตรพิสดารพอฉันต้องเอาLC_ALLการตั้งค่าที่ชัดเจนของ LC_ALLไม่ได้ตั้งค่าในสภาพแวดล้อมของฉัน (แต่LANGตั้งไว้ที่en_GB.UTF-8) ด้วยรหัสข้างต้นฉันได้รับข้อผิดพลาด“ sed: 1: "y / ۰۱۲۳۴۵۶۷۸۹ / ... ": การแปลงสตริงไม่ได้มีความยาวเท่ากัน "
Konrad Rudolph

15

สำหรับงูหลามมีunidecodeห้องสมุดซึ่งมีหน้าที่จัดการแปลงดังกล่าวโดยทั่วไป: https://pypi.python.org/pypi/Unidecode

ใน Python 2:

>>> from unidecode import unidecode
>>> unidecode(u"۰۱۲۳۴۵۶۷۸۹")
'0123456789'

ใน Python 3:

>>> from unidecode import unidecode
>>> unidecode("۰۱۲۳۴۵۶۷۸۹")
'0123456789'

เธรด SO ที่/programming//q/8087381/2261442อาจเกี่ยวข้องกัน

/ แก้ไข: ตามที่ Wander Nauta ชี้ให้เห็นในความคิดเห็นและตามที่กล่าวไว้ในหน้า Unidecode ยังมีรุ่นของเชลล์unidecode(ภายใต้/usr/local/bin/หากติดตั้งมากกว่าpip):

$ echo '۰۱۲۳۴۵۶۷۸۹' | unidecode
0123456789

2
ไลบรารี unidecode ยังจัดส่งยูทิลิตีที่เรียกว่า (น่าแปลกใจ) unidecodeซึ่งทำเช่นเดียวกับตัวอย่าง Python 3 ของคุณ เพียงแค่echo '۰۱۲۳۴۵۶۷۸۹' | unidecodeควรจะทำงาน
Wander Nauta

@Wander - แพ็คเกจ Debian ของ python-unidecode ไม่ได้จัดส่งโปรแกรมยูทิลิตี้ดังนั้นรูปแบบยาวอาจจำเป็นในแพลตฟอร์มดังกล่าว (ฉันไม่พบหนึ่งในแหล่ง tarball จากต้นน้ำดังนั้นโปรแกรมอาจเป็นสิ่งที่เพิ่มโดย การกระจายของคุณหรือไม่)
Toby Speight

@TobySpeight หากคุณติดตั้งโดยใช้ที่pipนั่น
phk

@TobySpeight ยูทิลิตี้อยู่ใน tarball ต้นน้ำเป็นunidecode/util.py- แปลกที่ Debian ไม่ได้รวมไว้ (แก้ไข: อ่าลึกลับได้รับการแก้ไขแพคเกจเดเบียนล้าสมัยและอายุมากกว่ายูทิลิตี้)
Wander Nauta

7

รุ่นทุบตีบริสุทธิ์:

#!/bin/bash

number="$1"

number=${number//۱/1}
number=${number//۲/2}
number=${number//۳/3}
number=${number//۴/4}
number=${number//۵/5}
number=${number//۶/6}
number=${number//۷/7}
number=${number//۸/8}
number=${number//۹/9}
number=${number//۰/0}

echo "Result is $number"

ทดสอบในเครื่อง Gentoo ของฉันแล้วใช้งานได้

./convert ۱۳۲
Result is 132

ทำแบบวนซ้ำให้รายการของอักขระ (จาก 0 ถึง 9) เพื่อทำการแปลง:

#!/bin/bash
conv() ( LC_ALL=en_US.UTF-8
         local n="$2"
         for ((i=0;i<${#1};i++)); do
              n=${n//"${1:i:1}"/"$i"}
         done
         printf '%s\n' "$n"
       )

conv "۰۱۲۳۴۵۶۷۸۹" "$1"

และใช้เป็น:

$ convert ۱۳۲
132

วิธีอื่น (ค่อนข้างมาก) โดยใช้grep:

#!/bin/bash

nums=$(echo "$1" | grep -o .)
result=()

for i in $nums
do
    case $i in
        ۱)
            result+=1
            ;;
        ۲)
            result+=2
            ;;
        ۳)
            result+=3
            ;;
        ۴)
            result+=4
            ;;
        ۵)
            result+=5
            ;;
        ۶)
            result+=6
            ;;
        ۷)
            result+=7
            ;;
        ۸)
            result+=8
            ;;
        ۹)
            result+=9
            ;;
        ۰)
            result+=0
            ;;
    esac
done
echo "Result is $result"

1
grepเพียวทุบตียกเว้น result=0ในความเป็นจริงผมไม่เข้าใจว่าสายหรือเหตุผลที่คุณไม่ได้ตั้งค่า คุณมีความระมัดระวังมากเกินไปในกรณีที่$1มีสิ่งอื่นนอกเหนือจากตัวเลข Farsi หรือไม่?
Kusalananda

@ Kusalananda บรรทัดนั้นอ่านตัวเลข Farsi เป็นตัวเลข ทำให้สามารถวนซ้ำได้
coffeMug

1
แทนง่ายสิบจะได้รับเร็วขึ้น ... number=${number//۱/1}ฯลฯ และจะหลีกเลี่ยงและecho grep
Kusalananda

1
@ Kusalananda Nice เปลี่ยนแล้ว ตอนนี้มันเป็นทุบตีบริสุทธิ์! ;-)
coffeMug

@coffeMug: ۱۳۲ คือ 132 no 123: D
بارپابابا

3

เนื่องจากiconvดูเหมือนจะไม่สามารถคลุกคลีสิ่งนี้ได้พอร์ตการโทรถัดไปคือการใช้trยูทิลิตีนี้:

$ echo "۲۱" | tr '۰۱۲۳۴۵۶۷۸۹' '0123456789'
21

tr แปลอักขระหนึ่งชุดเป็นอีกชุดหนึ่งดังนั้นเราจึงบอกให้แปลชุดหลัก Farsi เป็นชุดหลักละติน

แก้ไข : ในฐานะผู้ใช้ @cuonglm ชี้ให้เห็น นี้ต้องไม่ใช่ GNU trสำหรับตัวอย่างtrบน Mac และมันยังกำหนดให้มีการตั้งค่า$LC_CTYPEen_US.UTF-8


2
โปรดทราบว่าจะไม่ทำงานกับ GNU tr ซึ่งไม่รองรับอักขระหลายไบต์
cuonglm

1
พุทโธ่. Silly GNU ;-)
Kusalananda

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