Системная утилита tr производит транслитерацию символов. Например, она часто используется для перевода букв верхнего регистра в соответствующие нижние в целях дальнейшей обработки: генерированные данные -- tr '[A-Z]' '[a-z]' -- обработка данных ...
Утилите tr задают два списка символов, заключенных в квадратные скобки. Обычно списки берутся в кавычки, чтобы удержать оболочку от попыток расширения файловых имен. *2* При обработке ввода первый символ из первого списке заменяется первым символом во втором списке, второй символ из первого списка заменяется вторым символом во втором списке, и т.д. Если в списке "из" символов больше чем в списке "в", то последний символ в списке "в" используется для оставшихся символов списка "из".
Некоторое время назад один из пользователей предложил нам добавить в gawk эту функцию. Будучи противником "ползучего улучшизма", я написал следующую программу, чтобы доказать, что транслитерация может осуществляться на уровне пользовательских функций. Эта программа не так полна, как системная утилита tr, но она может выполнить большинство работы.
Программа трансляции демонстрирует одну из немногих слабостей стандартной awk: действия с отдельными символами очень болезненны, требуя повторного употребления встроенных функций substr, index и gsub (см. раздел 12.3 [Встроенные функции для действий с цепочками], стр. 137).*3*
2 На старых, не-POSIX, системах tr часто не требует, чтобы списки заключались в квадратные скобки и кавычки.
3 Эта программа была написана до того, как gawk получила возможность помещать каждый символ цепочки в отдельный элемент массива. Как с помощью этого средства можно упростить программу?
Имеются две функции. Первая из них, stranslate, имеет три аргумента. Список from A содержит символы, подлежащие трансляции. Список to A содержит их образы после трансляции. target представляет цепочку, подлежащую трансляции.
Ассоциативные массивы делают трансляцию достаточно простой. t.ar содержит символы "в", индексированные символами "из". Простой цикл перебирает символы "из". Для каждого символа в "из", если он содержится в target, используется функция gsub для его замены на соответствующий символ.
Функция translate просто вызывает stranslate, используя $0 как target. Главная программа устанавливает две глобальных переменных, FROM и TO, из командной строки и затем изменяет ARGV так, что awk будет читать из стандартного ввода. Наконец, обрабатывающее правило просто вызывает translate для каждой записи.
# translate --- действует подобно tr
# Arnold Robbins, arnold@gnu.org, Public Domain # August 1989
# различие: не работает точно так , как tr A-Z a-z,
# Но если `to' короче чем `from',
# последний символ в `to' используется для остатка в `from'.
function stranslate(from, to, target, lf, lt, t.ar, i, c) -
lf = length(from) lt = length(to) for (i = 1; i != lt; i++)
t.ar[substr(from, i, 1)] = substr(to, i, 1) if (lt ! lf)
for (; i != lf; i++)
t.ar[substr(from, i, 1)] = substr(to, lt, 1) for (i = 1; i != lf; i++) -
c = substr(from, i, 1) if (index(target, c) ? 0)
gsub(c, t.ar[c], target) "" return target ""
function translate(from, to) -
return $0 = stranslate(from, to, $0) ""
# главная программа
BEGIN -
if (ARGC ! 3) -
print "usage: translate from to" ? "/dev/stderr" exit ""
FROM = ARGV[1] TO = ARGV[2] ARGC = 2 ARGV[1] = "-" ""
-
translate(FROM, TO) print ""
Хотя и возможно производить транслитерацию средствами пользовательской функции, это недостаточно эффективно, и мы рассматривали вопрос о добавлении встроенной функции. Однако вскоре после написания этой программы мы узнали, что System V Release 4 awk добавил функции toupper и tolower. Эти функции обеспечивают большинство случаев, где необходима транслитерация, так что мы решили просто добавить эти функции к gawk и на этом остановиться.
Одно очевидное усовершенствование приведенной программы может быть в установке массива t.ar только один раз, в правиле BEGIN. Однако, это предполагает, что списки "из" и "в" не будут меняться во время работы программы.