UTF-8의 페르시아 숫자를 ASCII의 유럽 숫자로 어떻게 변환 할 수 있습니까?


16

페르시아 숫자 에서 유럽 숫자 ۰۱۲۳۴۵۶۷۸۹와 같습니다 0123456789.

페르시아 숫자 (in 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}
۰۱۲۳۴۵۶۷۸۹

즉, 마지막 16 진수는 10 진수 값입니다.

$ 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

매우 감사합니다, 이것은 매우 좋은 해결책이며,이 명령 printf '% d' ' "۰'에서 큰 따옴표를 사용하는 이유는 무엇입니까?
بارپابابا

@Babyy 큰 따옴표가 아니며 printf에 작은 따옴표로 시작하는 인수를 제공하는 방법 입니다. 로도 쓰일 수있었습니다 '"۰'. 인수가 작은 따옴표 '또는 큰 따옴표로 시작하면 printf가 UNICODE 코드 포인트를 제공하기 때문입니다 ". 이 앞의 링크 에서 "대표 문자가 작은 따옴표 또는 큰 따옴표 인 경우"텍스트를 검색하십시오.

@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 2016

왜 모든 것이 sed 스크립트 여야합니까? 우리 tr는이 정확한 목적을 위해 발명하지 않았습니까 ?
Kevin

3
@Kevin tr어디서나 작동하지 않는 방법에 대한 다른 답변을 참조하십시오 . 또한 일부 도구는 바이트 처리에 최적화되어 있고 다른 도구는 문자 처리에 최적화되어 있으며 유니 코드 (특히 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은 .

파이썬 2에서 :

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

파이썬 3에서 :

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

/programming//q/8087381/2261442 의 SO 스레드 가 관련 될 수 있습니다.

/ edit : Wander Nauta가 주석에서 지적했듯이 Unidecode 페이지에서 언급했듯이 셸 버전도 있습니다 unidecode( /usr/local/bin/설치된 경우 아래 pip).

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

2
unidecode 라이브러리에는 unidecodePython 3 스 니펫과 동일한 기능을 제공하는 (놀랍지도 않게) 유틸리티 가 포함되어 있습니다. 그냥 echo '۰۱۲۳۴۵۶۷۸۹' | unidecode작동해야합니다.
Wander Nauta

@Wander-python-unidecode의 데비안 패키지는 유틸리티 프로그램을 제공하지 않으므로 이러한 플랫폼에서 긴 형식이 필요할 수 있습니다 (업스트림에서 소스 tarball을 찾지 못 했으므로 프로그램은 다음과 같이 추가되었습니다. 배포판?)
Toby Speight

@TobySpeight 그것을 사용하여 설치하면 pip거기에 있습니다.
phk

@TobySpeight이 유틸리티는 unidecode/util.py데비안이 포함하지 않은 것이 이상하다. (편집 : 아, 수수께끼가 풀렸다. 데비안 패키지가 오래되었고 유틸리티보다 오래되었다.)
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"

내 젠투 머신에서 테스트했는데 작동합니다.

./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. $1Farsi 이외의 숫자가 포함 된 경우에 대비하여 지나치게 신중 합니까?
Kusalananda

@Kusalananda 그 줄은 Farsi 숫자를 숫자로 읽습니다. 루프 가능하게 만듭니다.
coffeMug

1
텐 간단한 치환은 ... 빨리이었을 것 number=${number//۱/1}등, 그리고을 피할 것 echo하고 grep.
Kusalananda

1
@Kusalananda 니스. 그것을 바꿨다. 이제 순수한 배쉬입니다! ;-)
coffeMug

@coffeMug : ۱۳۲ is 132 no 123 : D
بارپابابا

3

이 문제를 iconv해결할 수 없기 때문에 다음 호출 포트는 tr유틸리티 를 사용하는 것입니다 .

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

tr 한 문자 집합을 다른 문자 집합으로 변환하므로 단순히 Farsi 숫자 집합을 라틴 숫자 집합으로 변환하도록 지시합니다.

편집 : @cuonglm 사용자가 지적한대로. 이를 위해서는 Mac과 tr같은 비 GNU 가 tr필요하고 $LC_CTYPE로 설정되어 있어야합니다 en_US.UTF-8.


2
멀티 바이트 문자를 지원하지 않는 GNU tr에서는 작동하지 않습니다.
cuonglm 2016 년

1
어머. 바보 GNU. ;-)
Kusalananda

또한 로케일을와 같이 유니 코드를 지원하는 로케일로 설정해야합니다 en_US.utf8.
cuonglm 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.