Как очистить пользовательский ввод для правильного кодирования контента перед его сохранением?

У меня есть приложение, в котором пользователи вводят текст в формы.

Данные сохраняются в базе данных MySQL (сопоставление: utf8_general_ci), а затем выводятся в виде XML (кодировка: UTF-8).

Проблема в том, что люди склонны копировать и вставлять свою информацию из других источников, например, из документов Microsoft Word или PDF-файлов.

Этот входной текст часто содержит символы, которые не подходят для выходной кодировки, такие как «умные кавычки», взятые из документа в Кодировка Windows-1252

Очевидно, это вызывает проблемы при преобразовании или иной работе с XML, поскольку символы недопустимы.

Итак, как дезинфицировать ввод?

Раньше я использовал некоторые довольно грубые методы, такие как скрипт "de-moronize", который состоит из длинного списка операций поиска и замены.

Это все еще лучший способ сделать это? Есть ли другой способ?

Могу ли я просто установить атрибут accept-charset в форме, а браузер сделает это за меня?

Если да, то какие браузеры это сделают и могут ли возникнуть проблемы?

Кроме того, почему моя база данных принимает эти символы, которые являются зарезервированными/управляющими символами в UTF-8?

Как видите, я достаточно знаю о кодировках, чтобы понять, что у меня проблема, но сейчас я немного не в себе...

ТИА


person AmbroseChapel    schedule 15.04.2009    source источник


Ответы (3)


Этот входной текст часто содержит символы, которые неверны для выходной кодировки, такие как «умные кавычки», которые взяты из документа в кодировке Windows-1252.

«Умные кавычки» (байты 147 и 148 в cp1252) — это вполне допустимые символы Юникода, U+201C и U+201D. Ваше приложение должно иметь возможность беспрепятственно их обрабатывать; если нет, то вы делаете что-то не так, и, скорее всего, все не-ASCII-символы не будут работать.

Независимо от того, пришли ли символы от кого-то, кто их напечатал, или кто-то вставил их из Word, браузер должен отправлять символы в кодировке UTF-8 в ваше приложение, которое должно хранить те же байты UTF-8 в базе данных.

Если браузер не отправляет в UTF-8, скорее всего, вы не можете установить кодировку HTML-страницы, содержащей форму. Это можно сделать с помощью:

Content-Type: text/html;charset=utf-8

Заголовок HTTP и/или:

<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />

элемент в ‹head>.

Могу ли я просто установить атрибут accept-charset в форме, и браузер сделает это за меня?

Нет, accept-charset в основном бесполезен благодаря IE, который неверно интерпретирует его как означающее «попробуйте использовать этот набор символов, если тот, что на странице, не может кодировать символы, которые нам нужны», вместо «всегда используйте этот набор символов». Это означает, что если вы используете accept-charset, вы можете получить смесь кодировок, отправленных одновременно, без возможности выяснить, какая из них какая. Ницца!

почему моя база данных принимает эти символы, которые являются зарезервированными/управляющими символами в UTF-8?

В MySQL UTF-8 — это просто сопоставление, используемое для сравнения и упорядочения. Он по-прежнему хранит данные в виде байтов, и ему все равно, являются ли они недействительными последовательностями UTF-8.

В любом случае рекомендуется декодировать и проверять входящие последовательности UTF-8 в вашем приложении, потому что «короткие последовательности», недопустимые в современном Unicode, могут скрывать символ «‹», который будет распознаваться более старыми браузерами (по крайней мере, до IE6). SP2, Опера 7).

Расчетное время прибытия:

Итак, я ввел строку, содержащую 146 байт

Нет, вы ввели символ Юникода U+201B. Браузер работает с символами Unicode, а не с байтами, вплоть до момента, когда он должен отправить сериализованную форму на сервер. Именно тогда он решает, как преобразовать символы в байты, и если страница обрабатывается как UTF-8, он всегда будет выбирать UTF-8.

(Если это не UTF-8, браузеры склонны обманывать несовместимым со стандартами способом: для всех символов, которые не вписываются в кодировку, они кодируются в ссылки на символы HTML, такие как '''. Это неправильно. потому что теперь вы не можете отличить экранированный браузером '&' от реального, введенного пользователем '&', и это коварно неправильно, потому что если вы затем повторите ссылку как неэкранированный HTML, это выглядит так, как будто вы его получаете правильно, что на самом деле вы только что сделали большую старую дыру в безопасности.)

В базу он вошел под номером 146.

Действительно, байт ‘\x92’, а не ‘\xC2\x92’, ‘\xE2\x80\x99’ или ‘’’?

он вышел, когда я создал (в кодировке UTF-8) XML, как 146. Нет жалоб от браузера

Тогда он не вышел как единый 146-байтный. Браузер будет жаловаться, если в XML-файле будет указано «\x92». (Не файл HTML, в котором недопустимые последовательности UTF-8 отображаются как глиф с отсутствующим символом.)

Я подозреваю, что это ссылка на символ '', которая имеет правильный формат (хотя символ U+0092 является частью набора элементов управления C1, поэтому он не будет отображаться как что-то полезное). Если это то, что происходит, ваша страница формы все-таки не воспринимается как UTF-8, и вы страдаете от описанной выше проблемы автоматического экранирования браузером.

person bobince    schedule 15.04.2009
comment
ОК, байты 147 и 148 являются вполне допустимыми символами Unicode, U+201C и U+201D — это та часть, которую я не понимаю. Браузер не может знать кодировку вставленного теста. Откуда он знает, что это умные кавычки CP1252? Конечно, это всего лишь байты! Или использование 147-148 настолько необычно, что можно с уверенностью предположить это? - person AmbroseChapel; 16.04.2009
comment
Браузер работает не с байтами, а с символами Unicode. Вы вставляете символы Unicode U+201C (и др.) в текстовую область; браузер не знает и не заботится о том, как они были сохранены в приложении, из которого вы их скопировали (которое, вероятно, в любом случае было Unicode). - person bobince; 16.04.2009
comment
Это может быть отправка байтов 147/148, но это будет потому, что он думает, что кодировка страницы, содержащей форму, была cp1252, и поэтому кодировка, которую ваша форма хочет получить, — cp1252. Если вы не укажете иначе, по умолчанию может использоваться cp1252 (или другие системные кодовые страницы в других странах). - person bobince; 16.04.2009
comment
Я все еще в замешательстве. Если я вырезаю строку, содержащую байт 147, из Word, а затем вставляю ее в поле ввода браузера, что, по вашему мнению, делает браузер? Автоматически преобразовать его в U + 201C, потому что он знает, что это умная цитата, и у него есть внутреннее сопоставление 1252-UTF8? - person AmbroseChapel; 16.04.2009
comment
Кстати, кодировка страницы UTF-8. Возможно, я смогу отследить фактического пользователя и заставить его сказать мне, как именно он ввел текст, поскольку это внутреннее приложение. - person AmbroseChapel; 16.04.2009
comment
Значит, проблема возникает только у одного пользователя? Если вы сами вставите какой-нибудь не-ASCII (вот некоторые для проверки: «æšʩЖあ☃»), все пройдет нормально? - person bobince; 16.04.2009
comment
Да, это так. Итак, я ввел строку, содержащую 146 байт (правая фигурная одинарная кавычка CP-1252). Он вошел в базу данных как 146 и вышел, когда я создал (кодированный UTF-8) XML, как 146. Никаких жалоб со стороны браузера или моего редактора XML, несмотря на то, что 146/U+0092 является управляющим символом в UTF. -8. - person AmbroseChapel; 16.04.2009
comment
Ты прав! Я полагаю, это действительно связано с тем, что у меня нет правильных инструментов, чтобы смотреть на персонажей и определять, кто они на самом деле. Но когда я делаю sprintf(%X,ord($_)) для символов в этой строке, это действительно «\xE2\x80\x99» для умной кавычки! Спасибо. - person AmbroseChapel; 17.04.2009

Вы можете попробовать Perl-модуль Encode. Он поддерживает преобразование между несколькими наборами символов, включая UTF-8, конечно. Я только что проверил свою установку Perl, и она также поддерживает «cp1252», что, согласно Википедии, является просто другим названием Windows-1252. Вы можете проверить свою собственную установку с помощью следующего лайнера:

perl -MEncode -e 'print map {"$_\n"} Encode->encodings(":all");'
person Brian Ferris    schedule 15.04.2009

«Могу ли я просто установить в форме атрибут accept-charset, а браузер сделает это за меня?»

Только если вы готовы доверять «браузеру» — это может быть уместно в некоторых приложениях, но в целом оставляет вас открытым для вреда (или хуже).

(Также см. предупреждения bobince об IE...)

Иэн

person bigiain    schedule 16.04.2009