Используя sed, как регулярное выражение может сопоставлять китайские символы?

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

Итак, у меня есть, возможно, большой текстовый файл (> 1000 строк), содержащий китайские символы, с примерной строкой, например:

"ref#2-5-1.jpg#2#一些 <variable> 内容#pic##" (the Chinese just means "some content"). 

Все, что нужно изменить, это то, что между каждым символом должен быть вставлен пробел, если его еще нет:

"ref#2-5-1.jpg#2#一 些 <variable> 内 容#pic##".

Я наивно начал с простых вещей, таких как следующие, но совпадений нет вообще:

sed -e 's/\([\u4E00-\u9fff]\)/\1 /g' <test_utf_sed.txt > test_out.txt

где 4E00-9fff должны быть кодовым диапазоном китайского языка. Неудивительно, что это не сработало, поэтому я тоже хотел попробовать

sed -e 's/\([一-龻]\)/hello/g' <test_utf_sed.txt > test_out.txt

Это не удалось, потому что мой bash не может отображать (?) символ «一».

Затем я провел базовый тест, который также не удался:

sed -e 's/\(\u4E00\)/hello/g' <test_utf_sed.txt > test_out.txt //一
sed -e 's/\(\u4E9B\)/hello/g' <test_utf_sed.txt > test_out.txt //些

То же самое с другим обозначением для кодировки utf (найдено здесь в stackoverflow):

sed -e 's/\(\u'U+4E00\)/hello/g' <test_utf_sed.txt > test_out.txt

1) Является ли sed правильным выбором в качестве инструмента для работы с двухбайтовыми символами?

2) Способен ли sed вообще обрабатывать юникод, или мне нужен специальный переключатель?

3) Я не ищу обходное решение, подобное этому:

step1: insert space after each character 
  //like 's/\(.\)/\1 /g')
step2: remove space after each chacter which is not a Chinese character 
  //like 's/\([a-zA-Z0-9]\) /\1/g')

Я знаю, как это сделать, но это неэлегантно и подвержено ошибкам. Это должно быть возможно с использованием utf-8 в регулярном выражении в sed.

4) Моя среда — bash-3.2 на MacOS 10.6.8 (старая ОС).

5) Если вы знаете какие-либо указатели на некоторые открытые regEx-onliners в качестве библиотеки, работающей с китайским текстом или обработкой языка, было бы здорово поделиться.

Заранее большое спасибо, ваша помощь очень ценится!


person sweetnsour    schedule 20.04.2014    source источник
comment
stackoverflow.com/questions/8562354/   -  person ooga    schedule 21.04.2014


Ответы (2)


Perl имеет довольно хорошую поддержку для работы с Unicode. Это может быть лучше для вашей задачи, чем sed. Этот однострочник работает как ваш первый пример sed:

perl -CIOED -p -e 's/\p{Script_Extensions=Han}/$& /g' filename

-CIOED указывает Perl выполнять ввод-вывод в utf8. -p запускает данный код один раз для каждой строки входного файла, а затем печатает результат. -e указывает строку кода Perl для запуска. Дополнительную информацию см. в документации по аргументам командной строки.

В регулярном выражении используются именованные диапазоны для определения совпадающих символов.

Вы также можете прочитать документацию по Perl Unicode.

person Evan    schedule 20.04.2014
comment
Привет, Эван, большое спасибо, это сработало :) Наконец-то я добавил условие просмотра, чтобы убедиться, что пространство добавляется только в том случае, если его еще нет: 'perl -CIOED -p -e 's/\p{Block=CJK_Unified_Ideographs }**(?! )**/$& /g' имя файла' - person sweetnsour; 21.04.2014
comment
Спасибо за такой аккуратный ответ, потрясающе!! - person Yan King Yin; 16.08.2015

sed не понимает \u управляющих последовательностей (очевидно). Я не знаю, работает ли bash-3.2, но я думаю, что да; если да то можешь написать

sed $'s/\u4E9B/hello/g'

но вы все равно не сможете сделать спецификацию диапазона.

Однако, переведя в UTF-8 вручную, вы можете получить следующее расширенное регулярное выражение, которое, как я полагаю, будет соответствовать любой последовательности UTF-8 для символа в диапазоне U+4E00.. .U+9FFF:

(\xe4[\xb8-\xbf][\x80-\xbf]|[\xe5-\xe9][\x80-\xbf][\x80-\xbf])

(Но диапазоны символов будут работать, только если вы вызываете sed в однобайтовой локали, предпочтительно в локали C.)

С GNU sed вы получите расширенные регулярные выражения, если укажете флаг -r. Я считаю, что с MacOSX вам нужен флаг -E. Итак, вы можете попробовать:

LANG=C sed -E \
       $'s/(\xe4[\xb8-\xbf][\x80-\xbf]|[\xe5-\xe9][\x80-\xbf][\x80-\xbf])/\\1 /g' \
       <test_utf_sed.txt >test_out.txt

(Вышеупомянутое позволяет bash обрабатывать escape-последовательности \x. Если вы пропустите $, то sed будет обрабатывать escape-последовательности \x, но вам придется изменить замену с \\1 на \1. У меня нет Mac и нет старая версия bash, поэтому я действительно не знаю, делает ли ваш sed шестнадцатеричный экран или нет; я почти уверен, что ваш bash будет, но я не могу этого гарантировать.)


Кстати, получить кодировку utf-8 для этих символов не так уж и сложно; Я сделал это с помощью небольшого копирования и вставки из исходного сообщения. Например.:

$ hd <<<"一些"
00000000  e4 b8 80 e4 ba 9b 0a                              |.......|

Полезно знать, что весь диапазон идеограмм плоскости 0 (U+4E00...U+9FFF) имеет трехбайтовые коды, так что 一 — это E4 B8 80, а 些 — E4 BA 9B. (Конечно, 0A — это конец строки.)

person rici    schedule 21.04.2014
comment
Привет, Ричи, большое спасибо за изучение этого вопроса и перевод utf-8 в unicode! К сожалению, я получаю сообщение об ошибке (недопустимая последовательность байтов), хотя пробовал очень простую строку: 'sed -E $'s/(\xe4)/\1 /g' › имя файла'. Иначе я бы отследил идею дальше. - person sweetnsour; 21.04.2014
comment
@Lena: код UTF-8 для китайского символа имеет длину три байта; добавление пробела после первого байта (\xe4) создаст недопустимую последовательность байтов. Я не знаю, что вызвало эту ошибку, но это определенно не так. Для простого случая попробуйте \xe4\xba\x9b, который является кодом utf-8 для 些. Однако я забыл небольшую деталь в вызове sed; см. редактирование (если вам не все равно). - person rici; 22.04.2014
comment
низкий предварительный комментарий: еще раз спасибо! позвольте мне изучить это в выходные. я не профессиональный программист (уже), так что чтение занимает некоторое время. - person sweetnsour; 23.04.2014