Как прочитать строку UTF-8, учитывая ее длину в символах в простом C89? - PullRequest
6 голосов
/ 01 апреля 2011

Я пишу собственный кроссплатформенный минималистичный TCP-сервер на простом C89. (Но я также приму специфичный для POSIX ответ.)

Сервер работает со строками UTF-8, но никогда не заглядывает внутрь них. Он обрабатывает все строки как неизменяемые двоичные объекты.

Но теперь мне нужно принять строки UTF-8 от клиента, который не знает, как рассчитать их размер в байтах. Клиент может передавать только длину строки в символах. (Обновление: клиент в JavaScript, и "длина в символах" фактически равна тому, что возвращает String.length(). Я предполагаю, что это фактические символы UTF-8, а не что-то еще.)

Я не хочу добавлять тяжелые зависимости на мой крошечный сервер. Есть ли надежный и аккуратный способ прочитать эту дейтаграмму? (Ради этого вопроса, скажем, что он читается из FILE *.)

U<CRLF>       ; data type marker (actually read by dispatching code)
<SIZE><CRLF>  ; UTF-8 string size in characters
<DATA><CRLF>  ; data blob

Пример:

U
7
Юникод!

Обновление:

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

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

Ответы [ 6 ]

9 голосов
/ 01 апреля 2011

Довольно просто написать «читатель» UTF-8, учитывая информацию здесь ; UTF-8 был разработан таким образом, чтобы такие задачи были простыми.

По сути, вы начинаете читать символы, пока не прочитаете столько, сколько вам скажет клиент. Вы знаете, что прочитали целый символ с учетом определения кодировки UTF-8, а именно:

Если символ закодирован просто один байт, старший бит равен 0 и другие биты дают значение кода (в диапазон 0.127). Если персонаж кодируется последовательностью более чем одного байт, первый байт имеет столько же первые 1 биты как общее число байтов в последовательности, сопровождаемой бит «0» и последующие байты все отмечены лидирующим битом «10» шаблон.

2 голосов
/ 16 февраля 2012

Что ж, свойство length строк JavaScript, кажется, считает кодовые точки, а не символы, как вы можете видеть (но подождите! Это не совсем кодовые точки):

> s1='\u0061\u0301'
'á'
> s2='\u00E1'
'á'
> s1.length
2
> s2.length
1
>

Хотя это с V8. Оглядываясь вокруг, кажется, что на самом деле этого требует стандарт ECMAScript:

https://forums.teradata.com/blog/jasonstrimpel/2011/11/javascript-string-length-and-internationalizing-web-applications

Кроме того, проверяя ECMA-262, на страницах 40-41 PDF-файла написано «Длина строки - это количество элементов (то есть 16-битных значений) внутри нее», а затем переходим к разъяснению что элементы являются единицами UTF-16. К сожалению, это не совсем "кодовые точки". По сути, это делает свойство длины строки довольно бесполезным. Оглядываясь вокруг, я нахожу это:

Как определить, содержит ли строка многобайтовые символы в Javascript?

1 голос
/ 13 февраля 2012

Символы? Или кодовые точки? Два не одинаковы. Юникод это ... сложный. Вы можете сосчитать все эти разные вещи в строке UTF-8: длину в байтах, длину в кодовых точках, длину в символах, длину в глифах и длину в кластерах графем. Все они могут отличаться для любой заданной строки!

Мое первое желание - сказать, что сломанный клиент ушел. Но если вы не можете этого сделать, вам нужно спросить, что именно считает клиент. Простейшая вещь после байтов - это кодовые точки - это то, что кодирует UTF-8, в конце концов. После этого? символы, но вам нужно иметь таблицы составления кодовых точек, чтобы вы могли идентифицировать последовательности кодовых точек, которые составляют символ. Если клиент считает глифы или кластеры графем, то вас ждет мир боли. Но, скорее всего, клиент считает либо кодовые точки, либо символы. Если он считает кодовые точки, то просто подсчитайте байты с двоичными значениями 10xxxxxx и 0xxxxxxx (хотя вы, вероятно, захотите реализовать достаточно UTF-8 для защиты от слишком длинных последовательностей). Если он подсчитывает символы, то вам необходимо идентифицировать комбинированные метки и считать их как часть связанной некомбинированной кодовой точки.

0 голосов
/ 14 марта 2014

Это выглядит как раз то, что мне нужно. Жаль, что я нашел это раньше:

http://bjoern.hoehrmann.de/utf-8/decoder/dfa/

0 голосов
/ 01 апреля 2011

Если данные не могут содержать CRLF, кажется, что вы можете использовать CRLF в качестве разделителя кадров. Просто игнорируйте РАЗМЕР и читайте до CRLF.

0 голосов
/ 01 апреля 2011

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

  1. Читайте по одному байту за раз и собирайте их в символы довы получите соответствующее количество символов.

  2. Добавьте известный терминатор и полностью пропустите размер строки.Просто читайте по одному байту за раз, пока не прочитаете последовательность терминатора.

  3. Считайте количество байтов, перечисленных в заголовке (так как это минимальное число).Выясните, достаточно ли у вас персонажей.Если нет, прочитайте еще!

...