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

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

MS не планируют UTF-8 везде в .Net начать использовать?
А зачем это это нужно делать?
Уверен, везде останется UTF-16. Меня удивляет, что в IL для каких-то целей используется UTF-8.
И как же это всё относится к C#? Ну, константы же надо как-то представлять на уровне IL. Оказывается, способов представления тут два – в большинстве случаев используется UTF-16, но для аргументов конструктора атрибута – UTF-8.
Кто-нибудь знает, почему для аргументов конструктора атрибута используется UTF-8 вместо UTF-16?
Возможно я не прав, но логичным объяснением, является то что, в конструкторах атрибутов используются только константы.
Логичным продолжением вопроса является вопрос, а почему в конструкторе атрибутов используются константы? Ну, думаю дело в том, что значения атрибутов попадают в метаданные, а они формируются на этапе компиляции.
Я не очень понимаю, как из того, что данные атрибутов хранятся в метаданных сборки следует то, что строковые метаданные должны хранится в формате UTF-8 вместо UTF-16.
Полагаю, что для хранение метаданных, как и в il, используется UTF-8.
Похоже, что это сделано в целях оптимизации. Если посмотреть в спецификацию MSIL, а именно, на пункты «II.24.2.3 #Strings heap» и «II.24.2.4 #US and #Blob heaps», то становится ясно, что сборка хранит служебную информацию в UTF-8 формате(кстати, это отностися не только к пользовательским атрибутам, но и к версии самой сборки) в специальном блоке #Strings, а пользовательские строки в формате UTF-16 в другом специальном блоке #US(похоже это акроним от user strings).

Выглядит, как конфликт спецификаций C# и MSIL.
По части сети проблем нет. Все-таки по сети передаются байты, а не строки. Строки получаются лишь после интерпретации этих данных программой. Они лишь частный случай. С тем же успехом может прилететь половина байтового представления Int. Порядок интерпретации всегда на совести программы.
Каким образом input.Substring(splitPoint) может попасть посреди суррогатной пары, ведь splitPoint указан не в байтах, а в символах?

По-моему для воспроизведения проблемы тут надо сначала делать GetBytes, а потом полученный кусок байт разрывать посередине.
Легко это может попасть. Суррогатная пара — это два отдельных UTF-16 символа. Соответственно на уровне работы со строками нет никакой инфы о том, это суррогатная пара тут у нас или два отдельных символа.
Кстати, наверное, существует возможность определить свои методы работы со строками, которые рассматривают суррогатные пары как один символ.
Я далек от C#, но думаю такая возможность есть. Однако в этой ситуации мы будем вынужденны при каждом изменении строки парсить ее, чтобы понять есть ли в ней суррогатные пары или нет, что негативно скажется на скорости.
Суррогатная пара — это два отдельных UTF-16 символа.

Что-то тут не так. Выше по тексту сказано: «Если вам нужно представить в UTF-16 символ с кодом больше U+FFFF, то используются два слова: первая часть суррогатной пары (в диапазоне от 0xD800 до 0xDBFF) и вторая (0xDC00 … 0xDFFF)». То есть для представления одного символа используются два слова суррогатной пары. Одного символа же, а не двух.
Обычный символ UTF-16 — это два байта.
Суррогатная пара — это 4 байта. Два «обычных» символа.
Спрошу иначе — на каких конкретно данных приведённый выше «плохой код» выдаст неправильный результат?

Сколько байт в суррогатной паре — понятно из статьи. Непонятно то, сколько символов представляет такая суррогатная пара. Если один — тогда Substring() не должен его разбивать на два. Если два — тогда зачем называть их «суррогатной парой», почему просто не считать их двумя разными символами.

Насколько я понял из объяснений — первый элемент суррогатной пары не может быть интерпретирован как самостоятельный символ. То есть, он имеет логический смысл только как составляющая пары, стало быть пара представляет собой один символ, а не два, и соответственно должна интерпретироваться как один символ библиотечными методами работы со строками. Но это, конечно, если строка правильно сформирована, то есть содержит в себе валидное «символьное» значение.
«Должна быть» — сентенция для идеального мира.
А мы живем в мире сложном. И если делать методы работы со строками идеальными, то мы потеряем возможность работать со строкой как с массивом, а значит имеем проседание производительности там где не должны. Терять производительность ради 0.00001% программистов, которым реально нужно работать с сложным unicode — бессмысленное занятие
UCS-2 хватит всем. А кому не хватает — должны думать прежде чем использовать стандартные методы работы со строками.
В общем, я тут наконец погулил и вразумился. Оказалось, что если строка содержит суррогатные пары, то не только Substring, но и другие методы работы со строками будут давать странные результаты, поскольку работают именно с последовательностью 16-битных char, а не грубо говоря с «буквами» (не с логическим значением этих char'ов), то есть фактически не берут в расчёт кодировку. Получается, что для них суррогатрая пара — это действительно два разных символа, хотя для UTF-16 это один символ.

Таким образом, если некая «буква» представлена двумя char'ами, и я пишу слово из трёх таких букв, Length скажет, что длина такой строки — шесть символов. А не три. Так же себя ведут Substring, IndexOf и т.п. Собственно, об этом и статья — что строка C# это не то же самое что строка в кодировке UTF-16.

Спасибо, теперь вроде понятно))
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации