Показаны сообщения с ярлыком utf-8. Показать все сообщения
Показаны сообщения с ярлыком utf-8. Показать все сообщения

вторник, 8 февраля 2022 г.

Длина строк в Go

Этот пост родился из-за странного поведения валидатора Length из пакета ozzo-validation.

Следуя руководству, можно зарегистрировать, например, такой валидатор для строк:

data := "Некоторая строка"
err := validation.Validate(data,
    validation.Required,
    validation.Length(10, 16),
)
fmt.Println(err)

И так как в строке у нас кириллические символы, и более того - кодировка строки utf-8, то валидация не пропускает строку, падая с ошибкой "the length must be between 10 and 16". Почему так происходит, ведь валидируемая строка содержит ровно 16 символов?

Для того, чтобы понять почему валидация выдает ошибку - придется сходить в исходный код валидатора. Поиски приводят к тому, что для определения длины валидируемой строки используется метод Len() из типа reflect#Value. Путешествие дальше приводит нас к тому, что свойство Len вычисляется с помощью встроенной функции len. А как сообщает мануал:

The len built-in function returns the length of v, according to its type:
String: the number of bytes in v.

Следовательно, валидация на основании длины строки в байтах нас не устраивает, так как в случае многобайтных кодировок (коей является utf-8) число символов в строке не равно числу байт в этой же строке.

Хотелось бы, чтобы был метод типа utfLen(). И он есть в пакете unicode/utf8, правда, с немного другим названием - RuneCountInString. И о чудо, данный метод даже используется в валидаторе, если установить дополнительный флаг rune. И это можно сделать применив валидатор RuneLength.

Обновленный код:

data := "Некоторая строка"
err := validation.Validate(data,
    validation.Required,
    validation.RuneLength(10, 16),
)
fmt.Println(err)

Валидация отрабатывает как и ожидается, выводя <nil>.

И не забудьте написать для этого случая тест (ну или хотя бы комментарий), чтобы пытливый программист, использующий код после вас, не смог бы заменить RuneLength на Length, думая, что "и так сойдёт".

P.S. Больше разъяснений про длины строк можно почитать в этом ответе на stackoverflow.

P.P.S. Отличное видео на Youtube, раскрывающее суть кодировок.

вторник, 20 июня 2017 г.

Кодировка для DOMDocument

Сегодня покажу вам, ребята, немного "костыликов" при работе с xml и xpath.

Допустим, откуда-то издалека вы получаете кусок html-разметки и дальше ее парсите, вытягивая заголовки, какие-нибудь абзацы текстов и прочие слова. Как только вы все полученные данные захотите вывести - может произойти так что на странице вместо текста будут всем известные кракозябры. В чем же дело? А дело в том, что при создании объекта DOMDocument вы не указали кодировку, в которой пришли данные. Ну и так как вы это не указали, то DOMDocument подумал, что данные пришли скорее всего в ISO-8859-1.

Само собой это надо быстренько исправить. И написать вот так:

// Создаем объект
$dom = new DOMDocument('1.0', 'utf-8');

// Ваша строка с HTML-разметкой
$stringToParse = "......";

// Загружаем строку в DOMDocument, ЯВНО указывая ее кодировку
$dom->loadHTML('<?xml encoding="utf-8"?>' . $stringToParse);

// Делаем xpath-запрос, например
$xpath = new DOMXPath($dom);
$ps = $xpath->query('//p');

Причем абсолютно неважно как выглядит строка с html-разметкой - один ли там корневой элемент или несколько, DOMDocument сделает все как надо.

пятница, 16 марта 2012 г.

Массовая перекодировка

Для массовой перекодировки файлов можно воспользоваться следующими командами:
1. Перекодировка всех файлов в текущей директории (не рекурсивно):
for i in `ls -R *`; do iconv -f WINDOWS-1251 -t UTF-8 $i -o $i.new; done
2. Рекурсивная перекодировка всех файлов c расширением php, начиная с текущей директории:
find . -name "*.php" -exec iconv -f UTF-8 -t WINDOWS-1251 {} -o {}.new \;
В рамках освоения Питона были созданы два скрипта для перекодировки и изменения прав доступа (chmod). Качнуть можно из репозитория: https://github.com/u-mulder/python-fop, краткое описание в README.