интересно только программистам
Oct. 8th, 2006 11:01 pmПытаясь понять, почему моя программа не выводит текст так, как я хочу, обнаружил баг в одной из стандартных функций языка Перл. Подробности под катом - понятны будут только тем, кто знает Перл.
Баг в функции sprintf. Он проявляется, когда при выводе строки (аргумент %s), которая к тому же содержит широкие символы в UTF-8 (напр. русские буквы), используются одновременно оба ограничения на длину - минимальное и максимальное - и они равны. Ниже копирую вывод моей тест-программы, которая изолирует баг, и демонстрирует, при каких вызовах функции он происходит.
Первые две пары вызовов printf демонстрируют баг, остальные - его отсутствие при других аргументах.
printf('%-3.3s', 'абвгд'): |абв |
printf('%-3.3s', '01234'): |012|
printf('%3.3s', 'абвгд'): | абв|
printf('%3.3s', '01234'): |012|
printf('%3s', 'абвгд'): |абвгд|
printf('%3s', '01234'): |01234|
printf('%.3s', 'абвгд'): |абв|
printf('%.3s', '01234'): |012|
printf('%3.4s', 'абвгд'): |абвг|
printf('%3.4s', '01234'): |0123|
Проверил, что баг содержится и в самой последней версии языка. Теперь надо посмотреть, сколько осталось пороха в пороховницах и насколько будет легко его починить...
no subject
Date: 2006-10-08 09:11 pm (UTC)Не может ли это быть ошибкой libc?
no subject
Date: 2006-10-08 09:17 pm (UTC)Это точно не ошибка libc, sprintf перла не пользуется sprintf'ом системы, вместо этого все делает сам внутри.
no subject
Date: 2006-10-08 09:24 pm (UTC)Да, при таких вводных ошибка проявляется :(
no subject
Date: 2006-10-08 09:35 pm (UTC)Я вообще теперь стараюсь работать только в utf-8, и локаль у меня такая стоит, и вообще все :)
no subject
Date: 2006-10-08 09:55 pm (UTC)no subject
Date: 2006-10-08 10:12 pm (UTC)no subject
Date: 2006-10-08 10:28 pm (UTC)use utf8;
my $string = 'абвгд';
utf8::encode($string);
printf('%-3.3s', $string);
printf('%3.3s', $string);
no subject
Date: 2006-10-08 10:50 pm (UTC)no subject
Date: 2006-10-08 10:57 pm (UTC)no subject
Date: 2006-10-08 11:12 pm (UTC)no subject
Date: 2006-10-08 11:55 pm (UTC)no subject
Date: 2006-10-08 11:58 pm (UTC)no subject
Date: 2006-10-09 12:02 am (UTC)no subject
Date: 2006-10-09 12:54 am (UTC)no subject
Date: 2006-10-10 07:41 am (UTC)no subject
Date: 2006-10-10 08:33 am (UTC)no subject
Date: 2006-10-08 10:13 pm (UTC)no subject
Date: 2006-10-08 10:13 pm (UTC)no subject
Date: 2006-10-08 10:21 pm (UTC)no subject
Date: 2006-10-08 09:53 pm (UTC)print "_"; printf('%-3.3s', 'abcde');print "_\n";
print "_"; printf('%-3.3s', '01234');print "_\n";
print "_"; printf('%3.3s', 'abcde');print "_\n";
print "_"; printf('%3.3s', '01234');print "_\n";
Печатает(ожидаемо):
_abc_
_012_
_abc_
_012_
А следующий код:
print "_"; printf '<%.5s>', "truncated"; print "_\n";
print "_"; printf '<%.5s>', "012345678"; print "_\n\n;
print "_"; printf '<%10.5s>', "truncated"; print "_\n";
print "_"; printf '<%10.5s>', "012345678"; print "_\n\n";
print "_"; printf '<%-.5s>', "truncated"; print "_\n";
print "_"; printf '<%-.5s>', "012345678"; print "_\n\n";
print "_"; printf '<%-10.5s>', "truncated"; print "_\n";
print "_"; printf '<%-10.5s>', "012345678"; print "_\n\n";
Печатает (тоже ожидаемо):
__
_<01234>_
_< trunc>_
_< 01234>_
__
_<01234>_
__
_<01234 >_
А какая у Вас версия версия perl? У меня v5.8.4
no subject
Date: 2006-10-08 10:25 pm (UTC)Попробуйте русские буквы (начинаются с 0x410), расширенную латиницу (от 0x100), греческие (0x391), etc.
no subject
Date: 2006-10-08 10:31 pm (UTC)no subject
Date: 2006-10-08 10:39 pm (UTC)no subject
Date: 2006-10-08 10:51 pm (UTC)utf8::encode выключает utf8-флаг, делая строку "абвгд" идентичной строке "0123456789" (с точки зрения количества символов и того, как перл с ними работает).
no subject
Date: 2006-10-08 10:52 pm (UTC)no subject
Date: 2006-10-08 11:09 pm (UTC)--- perl-5.9.4-orig/sv.c 2006-08-15 15:37:41.000000000 +0300 +++ perl-5.9.4/sv.c 2006-10-09 01:06:31.000000000 +0200 @@ -8874,6 +8874,8 @@ } if (width) { /* fudge width (can't fudge elen) */ width += elen - sv_len_utf8(argsv); + if (has_precis && precis < elen) + width += precis - elen; } is_utf8 = TRUE; }Теперь послать им еще наверное неплохо бы.
no subject
Date: 2006-10-08 11:43 pm (UTC)no subject
Date: 2006-10-08 11:50 pm (UTC)--- sv.c.old 2006-10-09 01:29:33.000000000 +0200 +++ sv.c 2006-10-09 01:46:15.000000000 +0200 @@ -8873,7 +8873,13 @@ precis = p; } if (width) { /* fudge width (can't fudge elen) */ - width += elen - sv_len_utf8(argsv); + if (has_precis && precis < elen) { + I32 p = precis; /* in bytes now */ + sv_pos_b2u(argsv, &p); + width += precis - p; + } + else + width += elen - sv_len_utf8(argsv); } is_utf8 = TRUE; }no subject
Date: 2006-10-09 12:52 am (UTC)--- sv.c.old 2006-10-09 01:29:33.000000000 +0200 +++ sv.c 2006-10-09 02:40:58.000000000 +0200 @@ -8867,13 +8867,17 @@ else { eptr = SvPVx_const(argsv, elen); if (DO_UTF8(argsv)) { + I32 old_precis = precis; if (has_precis && precis < elen) { I32 p = precis; sv_pos_u2b(argsv, &p, 0); /* sticks at end */ precis = p; } if (width) { /* fudge width (can't fudge elen) */ - width += elen - sv_len_utf8(argsv); + if (has_precis && precis < elen) + width += precis - old_precis; + else + width += elen - sv_len_utf8(argsv); } is_utf8 = TRUE; }no subject
Date: 2006-10-09 01:13 am (UTC)open(IN, "<:encoding(utf-8)", $FILE) or die; while(<IN>) { ... }. Если в файле при этом испорченный utf (что в Сети встречается гораздо чаще, чем следовало бы), то 5.8.6-й перл может при чтении ошибочной строки свалиться в бесконечный цикл, печатая всё время utf8 "..." does not map to Unicode. Я пока не разобрался, исправлено ли это в более свежих версиях.no subject
Date: 2006-10-09 01:18 am (UTC)Видимо, читайте весь файл в память в байтах и переводите через Encode после чтения (или построчно).
no subject
Date: 2006-10-09 10:35 am (UTC)no subject
Date: 2006-10-09 03:45 pm (UTC)Хочется научить перл время от времени писать: фашизм поднимает голову at foo.pl line 14142 или там — поток убит кровавой гебней at bar.pl line 2718. У нас тут любят такие развлечения.
no subject
Date: 2006-10-09 03:56 pm (UTC)no subject
Date: 2006-10-09 04:19 pm (UTC)no subject
Date: 2006-10-09 04:22 pm (UTC)no subject
Date: 2006-10-11 08:46 am (UTC)Вам точно хочется с этим возиться? Я проверил, в 5.8.8 уже обе блохи исправлены. Иначе бы я и сам стал разбираться.
no subject
Date: 2006-10-11 08:57 am (UTC)no subject
Date: 2006-10-09 03:03 am (UTC)Прямо америку открыли для меня, честно. Занятно...
Ругательство при использовании юникода
Date: 2006-10-09 08:44 am (UTC)use charnames ':full';print "\x{0041}\N{WHITE SMILING FACE}"
то получаю на выходе букву А и смайлик, что вполне понятно, но кроме того, пёрл говорит "Wide character in print". Вот вопрос собственно такой --- зачем он это говорит, наверное что-то я не так делаю, но не понимаю, что именно?
Re: Ругательство при использовании юникода
Date: 2006-10-09 08:53 am (UTC)Re: Ругательство при использовании юникода
Date: 2006-10-09 09:13 am (UTC)no subject
Date: 2006-10-09 09:18 am (UTC)может вести куда угодно
Date: 2006-10-09 09:21 am (UTC)no subject
Date: 2006-10-09 11:00 am (UTC)no subject
Date: 2006-10-09 11:31 am (UTC)no subject
Date: 2006-10-09 11:53 am (UTC)no subject
Date: 2006-10-09 05:46 pm (UTC)Я тут тоже размечталась открыть журнал - но кто дает? В смысле я для начала хочу фрее попробовать, а мне в ответ - сначала заплатите.
А я в кантри три тысячи заплатила и ни разу не пошла.
Может фрее акоунт отменинили? И где взять мануал на все действия - открытие и последующая эксплуатация.
Тут есть ваш мобильник - но это я думаю уже слишком?
сто раз спасибо и извините.
Ирина
i_risha7@hotmail.com
no subject
Date: 2006-10-09 05:55 pm (UTC)Никто не отменял бесплатные аккаунты, попробуйте еще раз.
http://www.livejournal.com/manage/settings/
здесь вы можете выбрать русский язык просмотра и потом читать FAQ сайта: http://www.livejournal.com/support/faq.bml