Проверка строки Unicode на наличие пробела - байт на байт! - PullRequest
2 голосов
/ 30 октября 2010

Быстрый и грязный Q: Могу ли я с уверенностью предположить, что байт кодовой точки UTF-8, UTF-16 или UTF-32 (символ) будет не пробелом ASCIIсимвол (если кодовая точка не представляет один)?

Я объясню:

Скажите, что у меня есть строка в кодировке UTF-8.Эта строка содержит несколько символов, для хранения которых требуется более одного байта.Мне нужно выяснить, являются ли какие-либо символы в этой строке символами пробела ASCII (пробел, горизонтальная табуляция, вертикальная табуляция, возврат каретки, перевод строки и т. Д. - Юникод определяет еще несколько символов пробела, но забудьте о них).

Итак, я делаю цикл по строке и проверяю, совпадает ли какой-либо из байтов с байтами, которые определяют пробельные символы.Возьмите, например, 0D (hex) для возврата каретки.Обратите внимание, что мы говорим здесь байты, а не символы.

Будет ли это работать?Будут ли кодовые точки UTF-8, где первый байт будет 0D, а второй - что-то еще - и эта кодовая точка не представляет возврат каретки?Может быть, наоборот?Будут ли кодовые точки, где первый байт является чем-то странным, а второй (или третий, или четвертый) байт равен 0D - и эта кодовая точка не представляет возврат каретки?

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

Что касается UTF-16 и UTF-32, я сомневаюсь, что это сработает вообще, но я почти ничего не знаю об их деталях, поэтому не стесняйтесь удивлять меня там ...

Причина для этого дурацкого вопроса в том, что у меня есть проверка кода для пробелов, которая работает для ASCII, и мне нужно знать, может ли он сломаться в Unicode.У меня нет выбора, кроме как проверить побайтово по ряду причин.Я надеюсь, что обратная совместимость с ASCII может дать мне хотя бы бесплатную поддержку UTF-8.

Ответы [ 4 ]

7 голосов
/ 30 октября 2010

Для UTF-8, да, вы можете. Все не-ASCII-символы представлены байтами с установленным старшим битом, а все ASCII-символы имеют отмененный старший бит.

Для ясности, каждый байт в кодировке не-ASCII-символа имеет установленный старший бит; это по замыслу.

Вы никогда не должны работать с UTF-16 или UTF-32 на уровне байтов. Это почти наверняка не сработает. На самом деле многое сломается, поскольку каждый второй байт, вероятно, будет '\0' (если вы обычно не работаете на другом языке).

5 голосов
/ 30 октября 2010

В , правильно закодированном UTF-8, все символы ASCII будут кодироваться как один байт каждый, а числовое значение каждого байта будет равно кодовым точкам Unicode и ASCII.Кроме того, любой не ASCII-символ будет закодирован с использованием только байтов, для которых установлен восьмой бит.Следовательно, значение байта 0D будет всегда представлять возврат каретки, а не второй или третий байт многобайтовой последовательности UTF-8.

Однако иногда правила декодирования UTF-8злоупотреблять для хранения символов ASCII другими способами.Например, если вы берете двухбайтовую последовательность C0 A0 и UTF-8-декодируете ее, вы получите однобайтовое значение 20, которое является пробелом.(Каждый раз, когда вы находите байт C0 или C8, это первый байт двухбайтовой кодировки символа ASCII.) Я видел, как это было сделано для кодирования строк, которые первоначально предполагались как отдельные слова, но позже требования выросли доразрешить значению иметь пробелы.Чтобы не нарушать существующий код (который использовал такие вещи, как strtok и sscanf для распознавания полей, разделенных пробелами), значение было закодировано с использованием этого убитого UTF-8 вместо реального UTF-8.

Вы, вероятно, не должны беспокоиться об этом, хотя.Если входные данные для вашей программы используют этот формат, то ваш код, вероятно, в любом случае не предназначен для обнаружения специально закодированного пробела в этой точке, поэтому вы можете его игнорировать.

3 голосов
/ 30 октября 2010

Да, но см. Предостережение ниже о ловушках обработки потоков, не ориентированных на байты, таким образом.

Для UTF-8 любые байты продолжения всегда начинаются с битов 10, что делает их больше 0x7f, нет никаких шансов, что их можно принять за пробел ASCII.

Вы можете увидеть это в следующей таблице:

Range              Encoding  Binary value
-----------------  --------  --------------------------
U+000000-U+00007f  0xxxxxxx  0xxxxxxx

U+000080-U+0007ff  110yyyxx  00000yyy xxxxxxxx
                   10xxxxxx

U+000800-U+00ffff  1110yyyy  yyyyyyyy xxxxxxxx
                   10yyyyxx
                   10xxxxxx

U+010000-U+10ffff  11110zzz  000zzzzz yyyyyyyy xxxxxxxx
                   10zzyyyy
                   10yyyyxx
                   10xxxxxx

Вы можететакже обратите внимание на то, что для байтов без продолжения для кодовых точек вне диапазона ASCII также установлен старший бит, поэтому их никогда нельзя принять за пробел.

См. wikipedia UTF-8 для более подробной информации.

UTF-16 и UTF-32 не должны обрабатываться побайтово в первую очередь.Вы должны всегда обрабатывать само устройство, 16-битное или 32-битное значение.Если вы сделаете это, вы также покрыты.Если вы обрабатываете эти побайты, есть опасность, что вы найдете 0x20 байт, который не является пробелом (например, второй байт 16-битного значения UTF-16).

Для UTF-16, поскольку расширенные символы в этой кодировке формируются из суррогатной пары, отдельные значения которой находятся в диапазоне от 0xd800 до 0xdfff, нет никакой опасности, что эти компоненты суррогатной пары могут быть ошибочно приняты за пробелы.

См. википедию UTF-16 для получения более подробной информации.

Наконец, UTF-32 ( ссылка на википедию здесь ) достаточно большой, чтобы представлять всеКодовые точки Unicode, поэтому не требуется никакого специального кодирования.

0 голосов
/ 30 октября 2010

Настоятельно рекомендуется не работать с байтами при работе с Unicode.Две основные платформы (Java и .Net) изначально поддерживают юникод, а также предоставляют механизм для определения таких вещей.Например, в Java вы можете использовать методы isSpace () / isSpaceChar () / isWhitespace () класса Character для вашего варианта использования.

...