UTF-8'deki Farsça sayıları ASCII'deki Avrupa sayılarına nasıl dönüştürebilirim?


16

Farsça rakamlarda, Avrupa rakamlarına ۰۱۲۳۴۵۶۷۸۹eşittir 0123456789.

Farsça numarasını (in UTF-8) ASCII'ye nasıl dönüştürebilirim ?

Mesela ۲۱olmak istiyorum 21.


1
İlginç, echo "۰۱۲۳۴۵۶۷۸۹" | iconv -f UTF-8 -t ascii//TRANSLITbaşa çıkmıyor gibi görünüyor ...
Kusalananda

@Kusalananda NOT çalıştı
بارپابابا

3
@Kusalananda: Gerçekten beklenmedik mi? Anladığım kadarıyla iconv, sadece farklı kodlamalardaki karakterleri eşlemek için buradayız, ancak bunlar ASCII'de eşdeğeri olmayan karakterler (Doğu Arap rakamları), bunları yeterince benzer bir şeye dönüştürebilirsiniz, ancak tek yönlüdür.
phk

3
Neler iconvyapabildiğinden ve yapamadığından emin değildim . Kullanarak //TRANSLITyardımcı olacağını umuyordum , ama olmadı.
Kusalananda

1
Ayrıca siparişi tersine çevirmeniz mi gerekiyor? Arap rakamlarının küçük-endian sağdan sola yazıldığını ve Latin rakamlarının büyük-endian soldan sağa olduğunu (basılı veya ekranda benzer, ancak bellekte tersine çevrilmiş) biliyorum. Farsça aynı mı?
Toby Speight

Yanıtlar:


6

Farsça rakamların UNICODE kod noktasının ardışık olması ve 0'dan 9'a kadar sıralanması gerçeğinden yararlanabiliriz :

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

Bu, son onaltılık basamağın ondalık değer olduğu anlamına gelir:

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

Bu, basit döngüyü bir dönüştürme aracı yapar:

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

Olarak kullanmak:

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

$ sefr.sh ۲۰۱
201

$ sefr.sh ۲۱
21

Bu kodun Arapça ve Latin rakamlarını da (karışık olsa bile) dönüştürebileceğini unutmayın:

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

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

çok çok teşekkürler, bu çok güzel bir çözüm ,, ve sorum var ,, bu komut printf '% d' '"۰' neden çift tırnak kullanın?
بارپابابا

@Babyy Bu bir yolu, tek bir alıntı ile başlamak vermek Printf bir argümandır, bir çift tırnak değil: . Olarak da yazılabilirdi '"۰'. Bunun nedeni, argümanın tek bir tırnak 'veya çift tırnak ile başlaması durumunda printf'in UNICODE kod noktasına vermesidir ". "Baştaki karakter tek tırnak veya çift tırnak ise" metni için bu bağlantıdan biraz önce arama

@Babyy Kod Farsça, Arapça ve Latince'yi (karışık olsa bile) dönüştürmek için genişletildi.

27

Sabit bir sayı kümesi olduğundan, elle yapabilirsiniz:

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

(veya henüz GNU tr kullanmıyor trancak kullanmıyor )

en_US.utf8Karakter kümenizi sedtanımak için yerel ayarınızı (veya karakter kümesinin ait olduğu yerel ayara göre) ayarlamak gerekir.

İle perl:

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

LC_ALLHer tek unicode karakterin de bu şekilde değerlendirileceği şekilde ayarlanması gerekir sed, değil mi?
phk

@ phk: Evet, güncellemeye bakın.
cuonglm

Neden her şey bir sed senaryosu olmalı? trBu amaç için icat etmedik mi?
Kevin

3
@Kevin trHer yerde nasıl çalışmadığı ile ilgili diğer cevaba bakınız . Ayrıca, bazı araçların baytlarla başa çıkmak için optimize edildiğini, bazılarının ise karakterlerle başa çıktığını, Unicode (özellikle UTF-8) ile büyük bir fark yarattığını unutmayın.
phk

Bu benim için OS X 10.10.5 / GNU bash 4.3'te çalışmaz. Garip yeterince açık ayarını kaldırmak gerekir LC_ALL. LC_ALLçevremde de ayarlanmamış (ancak LANGolarak ayarlanmış en_GB.UTF-8). Yukarıdaki kodla, "sed: 1:" y / ۰۱۲۳۴۵۶۷۸۹ / ... "hatasını alıyorum: dönüşüm dizeleri aynı uzunlukta değil".
Konrad Rudolph

15

Python için unidecodegenel olarak bu tür dönüşümleri gerçekleştiren kütüphane vardır: https://pypi.python.org/pypi/Unidecode .

Python 2'de:

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

Python 3'te:

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

Https://stackoverflow.com/q/8087381/2261442 adresindeki SO iş parçacığı ilişkili olabilir.

/ edit: Wander Nauta'nın yorumlarda belirttiği gibi ve Unidecode sayfasında da belirtildiği gibi, bir kabuk sürümü de var unidecode( /usr/local/bin/eğer yüklenmişse altında pip):

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

2
Unidecode kütüphanesi ayrıca (şaşırtıcı olmayan bir şekilde) unidecodePython 3 snippet'inizle aynı şeyi yapan bir yardımcı program gönderir . Sadece echo '۰۱۲۳۴۵۶۷۸۹' | unidecodeçalışmalı.
Nauta Nauta

@Wander - python-unidecode'un Debian paketi yardımcı programı göndermez, bu nedenle bu tür platformlarda uzun form gerekli olabilir (yukarıdan kaynak tarballda bir tane bulamadım, bu yüzden belki de program tarafından eklenen bir şeydir) dağıtımınız?)
Toby Speight

@TobySpeight Eğer piporada kullanarak yüklerseniz .
phk

@TobySpeight Yardımcı program Debi'nin içermemesi unidecode/util.pygarip bir şekilde yukarı akış tarball'ında . (Düzenleme: Ah, gizem çözüldü. Debian paketi eski ve hizmet programından daha eski.)
Wander Nauta

7

Saf bir bash versiyonu:

#!/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 makinemde test yaptım ve işe yarıyor.

./convert ۱۳۲
Result is 132

Dönüştürülecek karakterlerin listesi (0'dan 9'a) verildiğinde bir döngü olarak yapılır:

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

Ve şu şekilde kullanılır:

$ convert ۱۳۲
132

Kullanmanın başka bir (oldukça aşırı) yolu 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
Saf Bash hariç grep. Aslında, bu çizgiyi ya da neden belirlemediğinizi anlamıyorum result=0. $1Farsça rakamlardan başka şeyler içeriyorsa , aşırı temkinli misiniz ?
Kusalananda

@Kusalananda bu satır Farsça rakamlarını rakamlara okur. Döngü yapabilmesini sağlar.
coffeMug

1
On basit oyuncu değişikliği daha hızlı olurdu ... number=${number//۱/1}vs. ve echove grep.
Kusalananda

1
@Kusalananda Güzel. Değiştirdi. Şimdi saf Bash! ;-)
coffeMug

@coffeMug: ۱۳۲ 132 hayır 123: D
بارپابابا

3

İtibaren iconvbu grok gibi olamaz, bir sonraki liman kullanmak olacaktır tryarar:

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

tr bir karakter kümesini diğerine çevirir, bu yüzden ona Farsça basamak kümesini Latin basamak kümesine çevirmesini söyleriz.

EDIT : @cuonglm kullanıcı işaret ettiği gibi. Bu tr, örneğin trMac'te GNU olmayan bir öğeyi gerektirir ve bunun da $LC_CTYPEayarlanmış olmasını gerektirir en_US.UTF-8.


2
Çok baytlı karakterleri desteklemeyen GNU tr ile çalışmayacağını unutmayın.
cuonglm

1
Aman. Aptal GNU. ;-)
Kusalananda

Ayrıca, yerel ayarınızı unicode'u destekleyen bir konuma ayarlamanız gerekir en_US.utf8.
cuonglm
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.