Проблема в том, что GNU tr
, который у вас есть в Linux, на самом деле не имеет концепции многобайтовых символов, а вместо этого работает байт за раз.
Справочная страница tr
и документация в Интернете говорят о символах, но это немного упрощает. Файл TODO
в пакете исходного кода упоминает этот элемент (выбран из coreutils 8.30):
Адаптируйте такие инструменты, как wc, tr, fmt и т.д. (Большую часть textutils), чтобы они были многобайтовыми. Проблема в том, что я хочу избежать дублирования значительных блоков логики, но я также хочу понести только минимальные (предпочтительно «нет») затраты при работе в однобайтовом режиме.
В системе Linux - даже с языковым стандартом UTF-8 (en_US.UTF-8
)- GNU tr
заменяет ä
как два "символа" (представление ä
в UTF-8 имеет два байта):
linux$ echo 'ä' | tr 'ä' 'x'
xx
В том же духе, смешивание ä
и ö
приводит к забавным результатам, поскольку их представления UTF-8 имеют общий байт:
linux$ echo 'ö' | tr ä x
x�
Или наоборот (x
здесь не применяется):
linux$ echo ab | tr ab äx
ä
И в вашем случае, GNU tr
принимает значение \377
как необработанное значение байта.
tr
на Mac отличается, он знает концепцию многобайтовых символов и действует соответственно:
mac$ echo 'ä' | tr ä x
x
mac$ echo ab | tr ab äx
äx
UTF-8 представление символа с числовым значением 0377 (U+00ff) - это два байта c3 bf
, так что вы получите это.
Самый простой способ иметь tr
работа байт в байт, чтобы иметь его использовать C локали, а не UTF-8 локали. Это дает забавное поведение снова:
$ echo 'ä' | LC_ALL=C tr 'ä' 'x'
xx
И в вашем случае вы можете использовать:
... | LC_ALL=C tr "\000" "\377"
Или вы можете использовать что-то вроде Perl для генерации этих байтов \xff
:
perl -e 'printf "\377" x 1000 for 1..100'