Как стать автором
Обновить

Комментарии 73

Такое лаконичное решение.
Долго думал. :)
Кстати, а для чего вообще нужно такое решение?
Ведь если я отдаю страницу в UTF-8,
то зачем мне предполагать, что форма придет в WIN1251?
То же самое, если наоборот.
Потому что кулхацкеры не дремлют.
Неправильная кодировка — она не должна ломать процедуру валидации.
Наверное, для этого они и проверяют.
Прежде всего потому что работать с UTF-8 не убедившись в том, что это UTF-8 нельзя. Можно много разных дров наломать.
Определить, что строка — однозначно UTF-8, а никакая другая, тоже нельзя.
Все эти проверки проверяют лишь может ли строка быть в UTF-8 или нет.

PS: прошу пример поломаных дров
Все эти проверки проверяют лишь может ли строка быть в UTF-8 или нет.
А большего и не надо. Если клиент сказал что текст будет в UTF-8, а прислал нечто в windows-1251, которое выглядит как UTF-8 — то он ССЗБ (сам себе злобный Буратина).

PS: прошу пример поломаных дров
Внизу — пример очевидный и довольно серъёзный, но уже не очень актуальный. Другой пример — спамфильтры. Вы можете использовать хитрые символы в URL, а невалидный UTF-8 обломает вам всю нормализацию. При этом вырезание лишнего (скажем тега <script>) склеит вам пресловутую точку (она из нескольких байт в UTF-8 состоит) и она «возродится из пепла». Да, можно на каждом шаге при обработке текста думать «а не создались ли у нас тут новые хитрые юникодные симвлы после того, как мы что вырезали/заменили/etc), а можно — убедиться что у нас на входе UTF-8 и больше уже об этом не задумываться. Кроме проверки UTF-8 на валидность хорошо бы ещё нормализацию провести — но это уже от конкретных алгоритмов зависит. Нормализация — штука медленная, сложная и неоднозначная, проверка UTF-8 на валидность — вещь быстрая и простая (особенно если через PCRE делать).
Эта проблема применима и к другим кодировкам, например: <<script>iframe… >
и решается только итерационным методом.
Если я предполагаю, что строка — UTF-8 то возродившиеся символы — будут рассмотрены в новой итерации вполне корректно.
Эта проблема применима и к другим кодировкам, например: <<script>iframe… >
Только к мультибайтовым. Когда вы удаляете из <<script>iframe… > <script> там не появляется новых символов (code points). Новые подстроки — да, появляются и за этим нужно сделать. Но новые символы — нет. В испорченной многобайтовой кодировке могут появляться новые символы — и это совсем другая проблема. Собственно корейцы там не только UTF-8 на валидность проверяют.
Если я предполагаю, что строка — UTF-8 то возродившиеся символы — будут рассмотрены в новой итерации вполне корректно.
У вас программа интерактивно по очереди вырезает сначала лишние теги, потом spam, потом снова теги, потом снова spam и так — пока процесс не сойдётся к чему-нибудь? Тоже вариант, конечно. Но доказать корректность этой деятельности куда сложнее, чем просто предварительно убедиться что у вас данные — не испорчены.
Ах да — после каждого вырезания тегов и обработки на spam нужно ещё раз нормализацию проводить! Тоже затраты…
пока процесс не сойдётся к чему-нибудь?

нет, только до тех пор, пока строка не изменится.
А расскажите в двух словах, как вы боретесь с вышеуказанной проблемой, пусть даже не мультибайтовой. Разве не итерационно?
> то ли китайцы, то ли индусы.
Я бы сказал, корейцы.
есть же велосипедисты, в манах по пхп и то есть ))

// Returns true if $string is valid UTF-8 and false otherwise.
function is_utf8($string) {
// From w3.org/International/questions/qa-forms-utf-8.html
return preg_match('%^(?:
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*$%xs', $string);
} // function is_utf8
Есть вариант без регекспов:

function is_utf8($string) { 
 for ($i=0; $i<strlen($string); $i++) { 
  if (ord($string[$i]) < 0x80) continue; 
  elseif ((ord($string[$i]) & 0xE0) == 0xC0) $n=1; 
  elseif ((ord($string[$i]) & 0xF0) == 0xE0) $n=2; 
  elseif ((ord($string[$i]) & 0xF8) == 0xF0) $n=3; 
  elseif ((ord($string[$i]) & 0xFC) == 0xF8) $n=4; 
  elseif ((ord($string[$i]) & 0xFE) == 0xFC) $n=5; 
  else return false; 
  for ($j=0; $j<$n; $j++) { 
   if ((++$i == strlen($string)) || ((ord($string[$i]) & 0xC0) != 0x80)) 
    return false; 
  } 
 } 
 return true; 
}
Поздравляю: вам удалось замедлить решение задачи в 5-10 раз! Продолжайте в том же духе.
Я же не утверджал, что это вариант лучше ;)
может вопрос конечно глупый но все же… а как узнать наверняка это UTF8 или нет?
Сильно-сильно в это верить
Ну либо вызовом iconv'а, либо вышеприведённым regexp'ом. С учётом того, что iconv работает быстрее в нормальном случае и в случае строк длиной в сотню байт и больше — я бы выбрал решение из топика.
Есть регулярка, описанная выше. Кроме того в движке википедии используется её упрощённый вариант, могу показать.
Да — этот способ гораздо лучше и iconv'а и регулярки. Которая к тому же умирает на больших строках.
С большой вероятностью можно так проверить: mb_check_encoding($str, 'UTF-8')
Для этого нужен модуль multibyte
Ну да, нужен. На сколько мне известно, на большинстве хостингов он включен. Хотя могу ошибаться.
Большинство — не все. Можно определять и без него, способ я указал выше.
Не стоит прогибаться под изменчивый мир ©
Говорю же — есть способ лучше. Кстати, песня та очень безрадостно кончается.
НЛО прилетело и опубликовало эту надпись здесь
Там безрадостны примеры как прогиба, так и перегиба :) А мораль да, позитивная: пусть старая джинса… раздолбанный бас — всё равно, не стоит :)
Ыы. По-моему, в вашей цитате лучше поставить-таки ударение в последнем слове. :)
НЛО прилетело и опубликовало эту надпись здесь
на C есть замечательная библиотека libenca, а так старый и добрый метод явное указание кодировки ;)
Причём тут указание кодировки? Если вам на вход подадут испорченный файл в котором между байтами 0xC0 и 0xBC будет написано слово "<script>" — то вы можете это слово вырезать, байты 0xC0 и 0xBC сольются и образуют символ "<" — а уже слово script после него может остаться. С валидной UTF-8 строкой такого случиться не может. Потому даже если вы точно знаете что у вас UTF-8 на входе, а не какая-либо другая кодировка — проверка лишней не будет.
(заинтересованно) А как это они так сольются, чтобы образовать символ "<"?
Очень просто. Посмотрите как получаются из UTF-8 символы в UTF-16 или UTF-32. И подумайте во что превратятся два байта 0xC0 и 0xBC :-) Конечно это случится только в том случае когда проверки не будет в Web-браузере — но лучше перебдеть, чем недобдеть и проверить строку дважды (на сервере и в браузере), чем ни разу (браузер понадеется на веб-сайт, веб-сайт — на браузер, в результате у посетителя вашего сайта деньги уведут — кому от этого хорошо будет?).
Ага, понятно. Правда, в спецификации:
An important note for developers of UTF-8 decoding routines: For security reasons, a UTF-8 decoder must not accept UTF-8 sequences that are longer than necessary to encode a character.

Собственно, ни один из подручных utf-8 агентов и не воспринял последовательность как <.
Ну там много может быть потенциальных бед. Особенно если вы позволяете использовать для ников и прочего символы из не-ASCII диапазона. Я просто самый очевидный пример привёл — сейчас он уже практически неактуален…
Пардон, зачем писать
return true;
else
return false;

если будет ретурн первый, то уже совсем не важно что будет в else, а это как никак целая строчка лишнего кода.
это значит что если убрать «else», то ничего не изменится.
Можно вообще написать
return iconv(параметры) == $str;
верно :)
Только лучше со скобками…
НЛО прилетело и опубликовало эту надпись здесь
В таком случае лучше использовать вариант с регулярным выражением
Если бы он работал — было бы лучше.
Спасибо! В избранное
В snippet'ы :)
Нууууу, ведь суть в том, что это работает? Да и код не особо не бредовый, бывает хуже :)
А что вам не нравится? Работает ведь. Скорость проверки сильно ниже регэкспа?
он просто был создан пхпшникам руснета :)
Я выборочно потыкал в Cи, Си++, Perl, там пусто. Так что не показательно.
govnokod.ru/best/asm?time=ever

Вот это точно показательно. У питона не было говнокода один день, у асма — никогда.
ruby там вообще нет — это не показатель
плохой код можно писать на любом языке
Ошибка: «корейцы» нужно написать с маленькой буквы.
Скажите пожалуйста как это работает?
my $cv = Text::Iconv->new('utf-8','windows-1251');
$File_path = $cv->convert($File_path);

my $cW = Text::Iconv->new('windows-1251','windows-1251');
$File_path = $cW->convert($File_path);
интересует переменная $cW конечно же.
# locale
LANG=ru_RU.CP1251
LC_CTYPE=«ru_RU.CP1251»
LC_NUMERIC=«ru_RU.CP1251»
LC_TIME=«ru_RU.CP1251»
LC_COLLATE=«ru_RU.CP1251»
LC_MONETARY=«ru_RU.CP1251»
LC_MESSAGES=«ru_RU.CP1251»
LC_PAPER=«ru_RU.CP1251»
LC_NAME=«ru_RU.CP1251»
LC_ADDRESS=«ru_RU.CP1251»
LC_TELEPHONE=«ru_RU.CP1251»
LC_MEASUREMENT=«ru_RU.CP1251»
LC_IDENTIFICATION=«ru_RU.CP1251»
LC_ALL=

Имена файлов в cp1251… все должно работать без этого велосипеда, но не работает!
Результат записывается в мускул, там тоже по дефолту 1251.
А кто такой cp1251?
;)
знаю только utf-8
Это конечно да, вы правы =)
Но речь идет о базе данных, файлы создаются через самбу виндовыми пользователями(т.е. имена в cp1251). Вложенность каталогов большая, размер 31 гигабайт, огроменное количество мелких файлов, все настолько ужасно что бекап занимает около 8 часов.
Если я буду использовать utf — перед архивацией потребуется копировать всю БД + рекурсивно пробегаться утилитой convmv… и обратные действия после восстановления бекапа, т.е. это дополнительное время и такой вариант, к сожалению не пойдет
(сейчас просто делаю tar -cvvf и все ок)

Собственно, вопрос стоял иначе — почему без строки
my $cW = Text::Iconv->new('windows-1251','windows-1251');
скрипт работает неправильно? Ведь локаль указана верно.
Ого, как всё запущено :)
Вы создаете имена файлов в других языках?
Совет — завязываете с этим.
alies-ы и title не кто не отменял ;)

Отвечу на последний вопрос — если бы не заморачивались c другими кодировками у вас бы не было вопросов ;)

Капча текстом на том сайте это сильно :))
самое интересное что на мой вопрос про определение кодировки «нормальным» способом народ выдал либо еще бредовей идею либо ссылался на эту же… в чем тогда прикол «кодобреда»? или я что-то не понимаю?
Это бред, но идейно гениальный, т.к. другие решения не лучше.
Улучшение habrahabr.ru/blogs/code_wtf/48661/#comment_1262856

К стати в перле работа с utf — это вообще убийство…
<troll>Не согласен: перл — один из самых проработанных в плане утф8 языков.</troll>
Простите, но вы не уточняли где нужно определить кодировку? Windows, Linux, язык програмирования или БД… с чем вы работаете? =)
Конкретно в линукс есть утилита enca. Я с ней сталкивался при написании скриптов на языке perl.
ну судя из статьи, речь идет о PHP
Мне интересно следить за темой «Кодобреда», но, как говорится, чужое заметить легко. Очень бы хотелось наряду с кодобредом видеть Ваше красивое решение того, что Вы высмеиваете :)
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории