Como posso converter numerais persas em UTF-8 para algarismos europeus em ASCII?

16

Em numerais persas, ۰۱۲۳۴۵۶۷۸۹ é equivalente a 0123456789 em dígitos europeus.

Como posso converter o número persa (em UTF-8 ) para ASCII?

Por exemplo, quero que ۲۱ se torne 21 .

    
por بارپابابا 19.06.2016 / 13:33

5 respostas

6

Podemos aproveitar o fato de que o ponto de código UNICODE de numerais persas é consecutivos e ordenados de 0 a 9 :

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

Isso significa que o último dígito hexadecimal é o valor decimal:

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

Isso torna esse loop simples uma ferramenta de conversão:

#!/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

Usando como:

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

$ sefr.sh ۲۰۱
201

$ sefr.sh ۲۱
21

Observe que esse código também pode converter numerais em árabe e latino (mesmo se misturados):

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

$ sefr.sh ٤٧0٠٦7١٣3٥۶٦۷
4700671335667
    
por 28.06.2016 / 05:09
27

Como é um conjunto fixo de números, você pode fazer isso manualmente:

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

(ou usando tr , mas não é GNU tr ainda)

A configuração da sua localidade para en_US.utf8 (ou melhor, para a localidade à qual os caracteres definidos pertencem) é necessária para que sed reconheça o conjunto de caracteres.

com perl :

$ echo "۲۱" |
  perl -CS -MUnicode::UCD=num -MUnicode::Normalize -lne 'print num(NFKD($_))'
21
    
por 19.06.2016 / 13:58
15

Para o Python, há a biblioteca unidecode que lida com essas conversões em geral: link .

No Python 2:

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

No Python 3:

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

O segmento SO no link pode estar relacionado.

/ edit: Como Wander Nauta apontou nos comentários e como mencionado na página Unidecode, há também uma versão do shell de unidecode (sob /usr/local/bin/ se instalado acima de pip ):

$ echo '۰۱۲۳۴۵۶۷۸۹' | unidecode
0123456789
    
por 19.06.2016 / 13:39
7

Uma versão pura do bash:

#!/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"

Já testei na minha máquina Gentoo e ela funciona.

./convert ۱۳۲
Result is 132

Feito como um loop, dada a lista de caracteres (de 0 a 9) para converter:

#!/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"

E usado como:

$ convert ۱۳۲
132

Outra forma (um pouco exagerada) usando 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"
    
por 20.06.2016 / 08:50
3

Como iconv parece não conseguir acertar isso, o próximo porto de escala seria usar o utilitário tr :

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

tr traduz um conjunto de caracteres para outro, por isso simplesmente dizemos para traduzir o conjunto de dígitos em farsi para o conjunto de dígitos latinos.

EDITAR : Como o usuário @cuonglm aponta. Isso requer não-GNU tr , por exemplo, o tr em um Mac, e também exige que $LC_CTYPE seja definido como en_US.UTF-8 .

    
por 19.06.2016 / 14:00