Модераторы: korob2001, ginnie

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> unicode в perl 
:(
    Опции темы
slavenk
Дата 16.7.2012, 15:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


TheAthlete
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.10.2009

Репутация: нет
Всего: нет



Здравствуйте!

Сейчас разбираюсь с юникодом в перл. У меня появилась такая проблема:

Код

my $bad_string_with_utf8_flag = "\x{d0}\x{90}";
say "utf8 flag: ".(utf8::is_utf8($bad_string_with_utf8_flag) ? 'is utf8' : 'no utf8');


Для данного кода должно выдать 

  utf8 flag: is utf8

а выдает:

  utf8 flag: no utf8

В чем может быть проблема? 

Пример писал по следующей статье: UTF Perl Practice, или как использовать UTF-8 в перле
PM MAIL ICQ Skype   Вверх
DurRandir
Дата 16.7.2012, 16:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 335
Регистрация: 27.9.2009

Репутация: 14
Всего: 17



Потому что вы записали в строку два байта. Перл "не догадывается", без вашей помощи, как эти байты интерпретировать) Чтобы строку из байт проинтерпретировать как строку, записанную в utf8, надо выполнить utf8::decode($bad_string_with_utf8_flag).
PM   Вверх
slavenk
Дата 16.7.2012, 16:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


TheAthlete
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.10.2009

Репутация: нет
Всего: нет



Т.е. получается, в приведенной статье ошибка:

Цитата

utf8::is_utf8 проверяет состояние флага. возвращает 1 в том случае, если на строке установлен utf-флаг:

utf8::is_utf8("\x{410}") = 1
utf8::is_utf8("\x{d0}\x{90}") = 1 # здесь не 1, а 0 (или undef)
utf8::is_utf8('А') = undef

PM MAIL ICQ Skype   Вверх
DurRandir
Дата 16.7.2012, 16:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 335
Регистрация: 27.9.2009

Репутация: 14
Всего: 17



Напишите автору)
PM   Вверх
slavenk
Дата 16.7.2012, 16:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


TheAthlete
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.10.2009

Репутация: нет
Всего: нет



Т.е. я так понял, что данная строка ("\x{d0}\x{90}") не имеет UTF-флага, UTF-флаг имеет каждый октет и чтобы Perl воспринимал данную строку как строку с UTF-флагом, нужно преобразовать ее в "нормальное" представление ("\x{410}") с помощью utf8::decode?
PM MAIL ICQ Skype   Вверх
slavenk
Дата 16.7.2012, 17:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


TheAthlete
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.10.2009

Репутация: нет
Всего: нет



Что-то я запутался!

Например, мы имеем три переменные:
Код

my $string_with_utf8_flag = "\x{410}";
my $string_without_utf8_flag = "А";
my $bad_string_without_utf8_flag = "\x{d0}\x{90}";


Названия переменных говорят сами за себя, т.е.
"\x{410}" - это настоящая Unicode строка, при проверке на utf8::is_utf8 выдает true
"А" - строка без utf-флага, т.е. по-сути набор байтов
"\x{d0}\x{90}" - "поломанная" строка без utf-флага

Но вот незадача - Data::Dumper выводит для строки $bad_string_without_utf8_flag строку 'А', а не "\x{d0}\x{90}". А все потому, что строка не имеет utf-флага. Для того, чтобы поднять utf-флаг, необходимо выполнить:

Код

utf8::upgrade($bad_string_without_utf8_flag);


Самое интерестное, что если к строке $string_without_utf8_flag применить функцию utf8::upgrade, мы получим строку "\x{d0}\x{90}" с поднятым utf-флагом

Добавлено через 10 минут и 24 секунды
Из данных рассуждений получается следующее: чтобы вышеприведенные пример из статьи был правильным (корректным), необходимо вручную выставить utf-флаг на строку "\x{d0}\x{90}".

Другими словами, для данного кода
Код

my $string_with_utf8_flag = "\x{410}";
my $string_without_utf8_flag = "А";
my $bad_string_with_utf8_flag = "\x{d0}\x{90}";
utf8::upgrade($bad_string_with_utf8_flag);


данная статья будет корректна, и Data::Dumper будет нормально отображать строку "\x{d0}\x{90}", а utf8::is_utf8 будет давать 1 (true)


Это сообщение отредактировал(а) slavenk - 16.7.2012, 17:40
PM MAIL ICQ Skype   Вверх
Pfailed
Дата 16.7.2012, 18:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 933
Регистрация: 19.7.2009

Репутация: 22
Всего: 39



perlunicode
Цитата

you can use the "\x{...}" notation for characters 0x100 and above. For characters below 0x100 you may get byte semantics instead of character semantics; see "The "Unicode Bug"


В статье пишется, что это два символа юникод. На самом деле это два байта.


--------------------
PM MAIL   Вверх
slavenk
Дата 17.7.2012, 10:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


TheAthlete
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.10.2009

Репутация: нет
Всего: нет



Действительно:

Цитата

utf8::encode($string) 

Converts in-place the character sequence to the corresponding octet sequence in UTF-X. That is, every (possibly wide) character gets replaced with a sequence of one or more characters that represent the individual UTF-X bytes of the character. The UTF8 flag is turned off. Returns nothing.
Код

    my $a = "\x{100}"; # $a contains one character, with ord 0x100
    utf8::encode($a);  # $a contains two characters, with ords 0xc4 and 0x80


Note that this function does not handle arbitrary encodings. Therefore Encode is recommended for the general purposes; see also Encode.
$success = utf8::decode($string) 

Attempts to convert in-place the octet sequence in UTF-X to the corresponding character sequence. That is, it replaces each sequence of characters in the string whose ords represent a valid UTF-X byte sequence, with the corresponding single character. The UTF-8 flag is turned on only if the source string contains multiple-byte UTF-X characters. If $string is invalid as UTF-X, returns false; otherwise returns true.
Код

    my $a = "\xc4\x80"; # $a contains two characters, with ords 0xc4 and 0x80
    utf8::decode($a);   # $a contains one character, with ord 0x100


Note that this function does not handle arbitrary encodings. Therefore Encode is recommended for the general purposes; see also Encode.


Единственно, не могу понять, как правильно перевести слово "ord" в комментарии
Код

# $a contains two characters, with ords 0xc4 and 0x80


PM MAIL ICQ Skype   Вверх
Pfailed
Дата 17.7.2012, 12:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 933
Регистрация: 19.7.2009

Репутация: 22
Всего: 39



По смыслу получается
Код

# $a содержит два символа, с кодами 0xc4 и 0x80

ords, видимо, образовано от функции ord(), или от слова, от которого образована функция ord().


Это сообщение отредактировал(а) Pfailed - 17.7.2012, 12:48


--------------------
PM MAIL   Вверх
DurRandir
Дата 17.7.2012, 13:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 335
Регистрация: 27.9.2009

Репутация: 14
Всего: 17



>слова, от которого образована функция ord
Ordinal smile
PM   Вверх
slavenk
Дата 17.7.2012, 13:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


TheAthlete
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.10.2009

Репутация: нет
Всего: нет



Цитата(Pfailed @  16.7.2012,  18:01 Найти цитируемый пост)
В статье пишется, что это два символа юникод. На самом деле это два байта.

Кстати, это не два байта, а два слова (четыре байта (октета)). Это как раз доказывает функция utf8::upgrade:

$num_octets = utf8::upgrade('А'); # $num_octets = 4
PM MAIL ICQ Skype   Вверх
Pfailed
Дата 17.7.2012, 14:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 933
Регистрация: 19.7.2009

Репутация: 22
Всего: 39



Насколько я понял upgrade() говорит сколько байт нужно что бы представить переданную последовательность, как буквы в utf-8. По два байта на каждый non-ASCII символ, итого 4.
Попробуйте функцию length() на этой последовательности, она должна возвратить 2.



--------------------
PM MAIL   Вверх
DurRandir
Дата 17.7.2012, 15:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 335
Регистрация: 27.9.2009

Репутация: 14
Всего: 17



>По два байта на каждый non-ASCII символ
Вообще, для перлового понимания utf-8, это 2-13. Но для общепринятого юникода - 2-4.

А length на utf-8 строке вернёт её длину в символах, а не байтах. Ради чего всё и затевается. Чтобы посмотреть, что творится внутри строки, надо использовать Devel::Peek'овский Dump.

Код

#невероятно!
perl -E '$a="\x{fffffff0}"; utf8::upgrade($a); use Devel::Peek; Dump $a; say "length is ", length $a' 
SV = PV(0x601078) at 0x61a348
  REFCNT = 1
  FLAGS = (POK,pPOK,UTF8)
  PV = 0x6020e0 "\376\203\277\277\277\277\260"\0 [UTF8 "\x{fffffff0}"]
  CUR = 7
  LEN = 16
length is 1

#"валидный" юникод
perl -E 'use charnames qw/:full/; $a="\N{TETRAGRAM FOR FULLNESS}"; utf8::upgrade($a); use Devel::Peek; Dump $a; say "length is ", length $a' 
SV = PV(0x601078) at 0x61a408
  REFCNT = 1
  FLAGS = (POK,pPOK,UTF8)
  PV = 0x6020e0 "\360\235\214\253"\0 [UTF8 "\x{1d32b}"]
  CUR = 4
  LEN = 16
length is 1

#в utf-8 это будет 4 байта, но 2 символа
perl -E '$a="\x{c4}\x{80}"; utf8::upgrade($a); use Devel::Peek; Dump $a; say "length is ", length $a'
SV = PV(0x601078) at 0x61a348
  REFCNT = 1
  FLAGS = (POK,pPOK,UTF8)
  PV = 0x6020e0 "\303\204\302\200"\0 [UTF8 "\x{c4}\x{80}"]
  CUR = 4
  LEN = 16
length is 2

#а вот для ascii-строки число байт и символов совпадут
perl -E '$a="\x{c4}\x{80}"; use Devel::Peek; Dump $a; say "length is ", length $a'
SV = PV(0x601078) at 0x61a348
  REFCNT = 1
  FLAGS = (POK,pPOK)
  PV = 0x6020e0 "\304\200"\0
  CUR = 2
  LEN = 16
length is 2



Это сообщение отредактировал(а) DurRandir - 17.7.2012, 15:27
PM   Вверх
slavenk
Дата 17.7.2012, 15:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


TheAthlete
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.10.2009

Репутация: нет
Всего: нет



Большое спасибо за код! Я так понял, только в последнем примере utf8-флаг не установлен. А CUR - это число байтов
PM MAIL ICQ Skype   Вверх
DurRandir
Дата 17.7.2012, 16:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 335
Регистрация: 27.9.2009

Репутация: 14
Всего: 17



>slavenk
Да
PM   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Perl"
korob2001
sharq
  • В этом разделе обсуждаются общие вопросы по языку Perl
  • Если ваш вопрос относится к системному программированию, задавайте его здесь
  • Если ваш вопрос относится к CGI программированию, задавайте его здесь
  • Интерпретатор Perl можно скачать здесь ActiveState, O'REILLY, The source for Perl
  • Справочное руководство "Установка perl-модулей", можно скачать здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, korob2001, sharq.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Perl: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1917 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.