Как преобразовать персидские цифры в кодировке UTF-8 в европейские цифры в кодировке ASCII?

Управляющие символы ASCII имеют определения из 1960-х годов (фактически предшествующие тому, что вы могли бы считать сетью ). Не все эти управляющие символы используются так, как тогда они были определены для телекоммуникационного оборудования.

В Unix-подобных системах нет необходимости в символе EOF ; никто не используется. Система может сообщать приложениям, сколько байтов находится в файле:

  • В некоторых других системах (например, в VMS, DOS, Windows) control-Z может действовать как маркер конца файла, поскольку в более ранний версии, система не могла сообщить некоторым приложениям, сколько байтов находится в файле.

    В случае VMS ограничение было связано со способом работы среды выполнения C. Приложения на ассемблере могли (и получили) получить файл правильного размера.

  • Системы Unix в оболочке обычно используют control-D, чтобы сообщить приложению, что достигнут конец ввода (файла), но control-D не сохраняется в файле.

В языке C EOF специально сделан -1 , чтобы указать, что это недопустимый символ. Стандартный ввод-вывод возвращает EOF , когда обнаруживается условие конца файла, а не специальный символ.

Между прочим, файлы не обязательно должны заканчиваться символом новой строки (перевод строки ASCII).Текстовые редакторы могут работать с файлами, которые содержат текст для печати, но не имеют завершающего символа новой строки.

16
03.03.2018, 21:46
5 ответов

Мы можем воспользоваться тем фактом, что кодовая точка 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
6
27.01.2020, 19:47

Поскольку это фиксированный набор чисел , вы можете сделать это вручную:

$ 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
27
27.01.2020, 19:47

Поскольку iconv не может это понять, следующим портом вызова будет использование утилиты tr :

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

tr переводит один набор символов в другой, поэтому мы просто говорим ему перевести набор цифр фарси в набор латинских цифр.

РЕДАКТИРОВАТЬ : как указывает пользователь @cuonglm. Для этого требуется не-GNU tr , например tr на Mac, а также требуется, чтобы для $ LC_CTYPE было установлено значение en_US.UTF- 8 .

3
27.01.2020, 19:47

Чистая версия 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"

Протестировано на моей машине 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"
7
27.01.2020, 19:47

Для Python существует библиотека 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 на https://stackoverflow.com/q/8087381/2261442 может быть связан.

/ edit: Как указал Вандер Наута в комментариях и как упоминалось на странице Unidecode, существует также версия оболочки unidecode (в разделе / usr / local / bin / при установке поверх pip ):

$ echo '۰۱۲۳۴۵۶۷۸۹' | unidecode
0123456789
15
27.01.2020, 19:47

Теги

Похожие вопросы