LINUX.ORG.RU

уточненный вариант вопроса «русские буквы в utf8-регекспах в perl»

 , ,


0

2

начнем с задачи — имеется текст, полученный ocr-ом книги; текст в общем-то находится в utf8, однако 100% гарантии нет

поскольку ocr-софт делает типичные ошибки, исправлять их удобнее скриптами на perl; в данном случае perl используется как такой «улучшенный sed», и именно в таком ключе надо отвечать на мой вопрос — т.е. выбирать решения попроще, покороче, понадежнее, и без «Set your PERL_UNICODE envariable to AS» — все должно находится внутри скрипта, а не в переменных окружения!

с учетом прошлого вопроса regexp-ы в perl для работы с русскими буквами в юникоде — как? вырисовывается такая схема:

фаза 1: проверка файла на юникодность, исправление не-юникодности, и *возможно* исправление символов, которых в книге точно нет

исправление не-юникодности должно делаться регекспами, *специально* написанными руками под каждый (редкий) случай неюникодности, а не автоматическим-пропуском-неюникодного-символа скажем, поэтому код должен выполнять регекспы побайтово:

while(<>){
   s/че-то-такое-не-представимое-в-утф8/исправленный вариант/;
   s/че-то-другое-не-представимое-в-утф8/тоже исправленный вариант/;
   ...
   s/£/f/; # <-- возможно это делать здесь, а возможно в фазе 2 ?

   die("место где случилась неюникодность") if /$_-строка-не-юникод/; 
   print;
}

фаза 2: исправление ошибок ocr-а уже в точно юникодном тексте

while(<>){
   s/: :/::/g;
   s/c([а-я])/с$1/g; # первое "с" латинское, второе -- русское
   # это лишь пример, на самом деле там все сложнее и по-другому
   ...
   print;
}

теперь сам вопрос: предложите необходимые шебанги, опции, use-ы и прочее, чтобы эта фигня работала *надежно* (фаза 1 — побайтово, фаза 2 — в юникоде)

как сделать «if /$_-строка-не-юникод/»?

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

p.s. это тема для медленного, неторопливого обсуждения, так что не ожидайте, что я на возникшие у вас вопросы отвечу сразу, как в прошлый раз

★★★★★

Последнее исправление: www_linux_org_ru (всего исправлений: 1)

Есть в коробке Encode все что тебе нужно. Вкратце перл оперирует своим юникодом - конвертишь в него, дальше регепсы будут щелкаться как по маслу.

P.S. Используй perl 5.14+ там много фиксов на регепсы уже сделали - сильно облегчит жизнь.

gh0stwizard ★★★★★
()
Последнее исправление: gh0stwizard (всего исправлений: 1)
Ответ на: комментарий от gh0stwizard

Есть в коробке Encode все что тебе нужно. Вкратце перл оперирует своим юникодом - конвертишь в него,

тогда видимо мне нужна ссылка на программу-заготовку «конвертим в свой юникод, интеллектуально исправляем ошибки, которые не конвертируются автоматически»

www_linux_org_ru ★★★★★
() автор топика
Последнее исправление: www_linux_org_ru (всего исправлений: 1)

без «Set your PERL_UNICODE envariable to AS» — все должно находится внутри скрипта, а не в переменных окружения!

use open qw(:std :utf8);

Не до конца представляю, что значит «неюникодность», баг в OCR? Оно же должно выдавать валидный текст.

x3al ★★★★★
()
Ответ на: комментарий от x3al

да, должно, но на случай, если глюканет, должна быть подстраховка

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от www_linux_org_ru

1. http://search.cpan.org/perldoc?Encode

1.1 Проверка:

$octets = encode_utf8($string);

Equivalent to $octets = encode(«utf8», $string). The characters in $string are encoded in Perl's internal format, and the result is returned as a sequence of octets. Because all possible characters in Perl have a (loose, not strict) UTF-8 representation, this function cannot fail.

Но тебе нужно следующее:

$string = decode_utf8($octets [, CHECK]);

Equivalent to $string = decode(«utf8», $octets [, CHECK]). The sequence of octets represented by $octets is decoded from UTF-8 into a sequence of logical characters. Because not all sequences of octets are valid UTF-8, it is quite possible for this function to fail. For CHECK, see «Handling Malformed Data».

Смотрим, что ставить в CHECK, тебе подойдет это:

I<CHECK> = Encode::FB_CROAK ( == 1)

If CHECK is 1, methods immediately die with an error message. Therefore, when CHECK is 1, you should trap exceptions with eval{}, unless you really want to let it die.

2. Матчасть:

2.1. http://perldoc.perl.org/Encode.html

2.2. О кодировках в регепсах: http://perldoc.perl.org/perlre.html

gh0stwizard ★★★★★
()
Последнее исправление: gh0stwizard (всего исправлений: 3)
Ответ на: комментарий от x3al

Оно же должно выдавать валидный текст.

сейчас пока выдает валидный текст, поэтому я вообще без фазы 1 обхожусь, так что весь вопрос — задел на будущее

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от gh0stwizard

Стоит ещё упомянуть о coderef for CHECK:

As of Encode 2.12 CHECK can also be a code reference which takes the ord value of unmapped caharacter as an argument and returns a string that represents the fallback character. For instance,

$ascii = encode(«ascii», $utf8, sub{ sprintf «<U+%04X>», shift });

Acts like FB_PERLQQ but <U+XXXX> is used instead of \x{XXXX}.

www_linux_org_ru, это позволит исправить повреждённый текст, но не указать позицию.

AITap ★★★★★
()
Ответ на: комментарий от AITap

ясно

по-моему, регекс для юникода тут: http://unix.stackexchange.com/questions/6516/filtering-invalid-utf8 вовсе не слишком страшен, так что я видимо заюзаю его, причем модифицирую, т.к. из 3-байтовых мне почти никто не нужен

#!/usr/bin/perl -W -C0

$utf8 = '.......';

while(<>) {
   die("non-unicode marked by --> at byte ".length($1)." in: $1-->$2\n") if /^($utf8)(.*)/ && length($2)>0;
   print;
}

-C0 хватит или еще что-то надо написать для работы исключительно с байтами?

www_linux_org_ru ★★★★★
() автор топика
Ответ на: комментарий от www_linux_org_ru

Должно хватить. Даже без -C0 этот скрипт должен работать с байтами (по идее).

Кстати, regexp можно обернуть в qr//. При этом получается скомпилированный regexp, и цикл выполняется быстрее:

$utf8 = qr/^(...-utf8-regexp-...)(.*)/;
...;
die if /$utf8/;

AITap ★★★★★
()

я посоветую еще глянуть на Encode::Guess. Достаточно хорошо умеет угадывать кодировку.

bvn13 ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

> -C0 хватит или еще что-то надо написать для работы исключительно с байтами?

можно и без -C0:

no encoding;
no utf8;
use bytes;
binmode STDIN, ':raw';
binmode STDOUT, ':raw';
arsi ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.