Скрипт транслитерации для оболочки linux

У меня есть несколько файлов .txt, содержащих текст в алфавите; Я хочу транслитерировать текст в другой алфавит; некоторые символы алфавита1 соотносятся с символами алфавита2 (т. е. a становится e), а другие — 1:2 (т. е. x становится гл).

Я хотел бы сделать это с помощью простого скрипта для оболочки Linux.

С помощью tr или sed я могу преобразовать символы 1:1:

sed -f y/abcdefghijklmnopqrstuvwxyz/nopqrstuvwxyzabcdefghijklm/

a станет n, b станет o и так далее (я думаю, это шифр Цезаря)

Но как мне работать с символами 1:2?


person Community    schedule 16.08.2014    source источник


Ответы (4)


Не ответ, просто чтобы показать более краткий идиоматический способ заполнения массива table[] из ответа @konsolebox, как обсуждалось в соответствующих комментариях:

BEGIN {
    split("a  e b", old)
    split("x ch o", new)
    for (i in old)
        table[old[i]] = new[i]
    FS = OFS = ""
}

поэтому сопоставление старых и новых символов ясно показано тем, что символ в первом split() сопоставляется с символами под ним, а для любого другого сопоставления, которое вы хотите, вам просто нужно изменить строку (строки) в split(), а не изменять 26-кратные явные назначения для таблицы [].

Вы даже можете создать общий скрипт для сопоставления и просто передать старые и новые строки в качестве переменных:

BEGIN {
    split(o, old)
    split(n, new)
    for (i in old)
        table[old[i]] = new[i]
    FS = OFS = ""
}

затем в оболочке что-то вроде этого:

old="a  e b"
new="x ch o"
awk -v o="$old" -v b="$new" -f script.awk file

и вы можете защитить себя от собственных ошибок, заполняя строки, например:

BEGIN {
    numOld = split(o, old)
    numNew = split(n, new)

    if (numOld != numNew) {
        printf "ERROR: #old vals (%d) != #new vals (%d)\n", numOld, numNew | "cat>&1"
        exit 1
    }

    for (i=1; i <= numOld; i++) {
        if (old[i] in table) {
            printf "ERROR: \"%s\" duplicated at position %d in old string\n", old[i], i | "cat>&2"
            exit 1
        }
        if (newvals[new[i]]++) {
            printf "WARNING: \"%s\" duplicated at position %d in new string\n", new[i], i | "cat>&2"
        }
        table[old[i]] = new[i]
    }
}

Разве не было бы хорошо знать, если бы вы написали, что b отображается в x, а затем по ошибке написали, что b отображается в y? Вышеупомянутое действительно лучший способ сделать это, но, конечно, ваш выбор.

Вот одно полное решение, как обсуждалось в комментариях ниже.

BEGIN {
    numOld = split("a  e b", old)
    numNew = split("x ch o", new)

    if (numOld != numNew) {
        printf "ERROR: #old vals (%d) != #new vals (%d)\n", numOld, numNew | "cat>&1"
        exit 1
    }

    for (i=1; i <= numOld; i++) {
        if (old[i] in table) {
            printf "ERROR: \"%s\" duplicated at position %d in old string\n", old[i], i | "cat>&2"
            exit 1
        }
        if (newvals[new[i]]++) {
            printf "WARNING: \"%s\" duplicated at position %d in new string\n", new[i], i | "cat>&2"
        }
        map[old[i]] = new[i]
    }

    FS = OFS = ""
}
{
    for (i = 1; i <= NF; ++i) {
        if ($i in map) {
            $i = map[$i]
        }
    }
    print
}

Я переименовал массив table в map только потому, что iMHO лучше отражает назначение массива.

сохраните вышеуказанное в файле script.awk и запустите его как awk -f script.awk inputfile

person Ed Morton    schedule 17.08.2014
comment
Я снова попробовал ваши коды, но они не дают результата; может я что-то пропустил? Что я сделал: скопировал код в новый файл с именем script.awk; запустите скрипт, как было предложено. Я не получаю ни ошибок, ни вывода. - person ; 17.08.2014
comment
Я только что показал, как по-другому заполнить таблицу сопоставления, вам все еще нужна остальная часть скрипта, опубликованного @konsolebox, чтобы действительно что-то делать с этим сопоставлением. Подождите, и я обновлю его полным решением. - person Ed Morton; 17.08.2014
comment
Теперь он выводит тот же текст ввода. Я скопировал ваш новый код в новый файл, затем в оболочке сделал: echo ae | awk -f скрипт.awk. Выход был: ae - person ; 17.08.2014
comment
Забыл добавить в настройки ФС и ОФС, когда собирал полное решение, сейчас обновил. - person Ed Morton; 17.08.2014
comment
Теперь это работает! Большое спасибо; Мне нравится его способность искать ошибки - person ; 17.08.2014

Используя Авк:

#!/usr/bin/awk -f
BEGIN {
    FS = OFS = ""
    table["a"] = "e"
    table["x"] = "ch"
    # and so on...
}
{
    for (i = 1; i <= NF; ++i) {
        if ($i in table) {
            $i = table[$i]
        }
    }
}
1

Применение:

awk -f script.awk file

Тест:

# echo "the quick brown fox jumps over the lazy dog" | awk -f script.awk
the quick brown foch jumps over the lezy dog
person konsolebox    schedule 16.08.2014
comment
Идеальный! Спасибо большое! - person ; 16.08.2014
comment
+1, но вместо того, чтобы явно заполнять таблицу, сделайте это, чтобы сохранить избыточное кодирование: split("a e x ch ...",t,/ /); for (i=1; i in t; i+=2) table[t[i]] = t[i+1]. - person Ed Morton; 17.08.2014
comment
@EdMorton: спасибо, но я не мог заставить это работать; и, тем не менее, мне на самом деле нравится идея явного заполнения таблицы (см. мой комментарий к @TomFenech) - person ; 17.08.2014
comment
@mus_siluanus, если вы расскажете нам, почему у вас не получилось заставить это работать, мы можем вам помочь. Даже если вы не используете это сейчас, это обычная идиома awk для заполнения массивов начальными значениями, поэтому вы, вероятно, захотите сделать это в какой-то момент. Если вы предпочитаете, вы можете иметь 2 массива, заполненных один относительно другого. Я добавлю ответ, чтобы показать вам, как это выглядит в формате. - person Ed Morton; 17.08.2014

Это можно сделать довольно лаконично, используя однострочник Perl:

perl -pe '%h=(a=>"xy",c=>"z"); s/(.)/defined $h{$1} ? $h{$1} : $1/eg'

или эквивалентно (спасибо, Джейпал):

perl -pe '%h=(a=>"xy",c=>"z"); s|(.)|$h{$1}//=$1|eg'

%h — это хеш, содержащий символы (ключи) и их замены (значения). s — это команда замены (как в sed). Модификатор g означает, что замена является глобальной, а e означает, что замещающая часть оценивается как выражение. Он захватывает каждый символ один за другим и заменяет их значением в хэше, если он существует, в противном случае сохраняет исходное значение. Переключатель -p означает, что каждая строка ввода печатается автоматически.

Тестирование:

$ perl -pe '%h=(a=>"xy",c=>"z"); s|(.)|$h{$1}//=$1|eg' <<<"abc"
xybz
person Tom Fenech    schedule 16.08.2014
comment
Большое спасибо! Мне нравится идея использования однострочника. Но я предпочитаю сценарий @konsolebox, потому что для длинных списков замен (как в транслитерациях) его подход даст более четкое представление о том, что я буду делать... своего рода красивая встроенная карта символов... - person ; 17.08.2014
comment
@glenn спасибо за редактирование - я предполагаю, что двойная кавычка в середине a=">xy" была опечаткой? Казалось, что это работает в первую очередь, что, я думаю, является просто признаком использования однострочника. - person Tom Fenech; 17.08.2014
comment
Именно по обоим пунктам. С use strict можно было бы увидеть Bareword "z" not allowed while "strict subs" in use - person glenn jackman; 17.08.2014
comment
@TomFenech Можно сократить до perl -pe'%h=(a=>"xy",b=>"z");s|(.)|$h{$1}//=$1|eg' <<<"abc". //= был введен после 5.8, поэтому должен работать, если не используется древний perl . - person jaypal singh; 17.08.2014

Использование sed.

Напишите файл transliterate.sed, содержащий:

s/a/e/g
s/x/ch/g

а затем запустите из командной строки, чтобы получить транслитерированный output.txt из input.txt:

sed -f transliterate.sed input.txt > output.txt

Если вам это нужно чаще, подумайте о том, чтобы добавить #!/bin/sed -f в качестве первой строки и сделать исполняемый файл с помощью chmod 744 transliterate.sed, как описано в Википедии. страница для sed.

person mgoni    schedule 26.04.2019