Как я могу хранить символы UTF-16 в базе данных Postgres?

Я пытаюсь сохранить некоторый текст (например, č) в базе данных Postgres, однако при получении этого значения оно отображается на экране как ?. Я не уверен, почему он это делает, у меня сложилось впечатление, что это символ, который не поддерживается в UTF-8, но был в UTF-8, однако, судя по первому ответу, это неверное предположение .

Оригинальный вопрос (который все еще может быть в силе):

Я читал о суррогатных парах UTF-8, которые могут достичь того, что мне нужно, и я видел несколько примеров, связанных с stringinfo объектом TextElementEnumerators, но я не смог разработать практическое доказательство концепции.

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

Обновленный вопрос: почему символ č возвращается из базы данных в виде вопросительного знака?

Мы используем NPGSQL для доступа к базе данных и VB.Net.


person Mr Shoubs    schedule 09.12.2011    source источник
comment
Используйте другой инструмент, например PgAdmin III, чтобы увидеть, правильно ли вставлен текст (в этом случае PgAdmin III увидит его нормально) или искажен при вставке. Это поможет вам выяснить, читает ли ваша ошибка текст или вставляя его в первую очередь.   -  person Craig Ringer    schedule 10.12.2011
comment
Кроме того, если вы вырезаете и изменяете текст UTF-8, проверьте свой код на предположения, что 1 байт = 1 символу, потому что это не допустимо для UTF-8. То же самое: 2 байта = 1 символ для UTF-16. Разбиение расширенного символа UTF-8 приведет к всевозможным странным результатам.   -  person Craig Ringer    schedule 10.12.2011
comment
Извинения. Удалили мой ответ. Я использовал два варианта SQL, когда столкнулся с чем-то похожим. Должно быть, это был другой.   -  person Philip Couling    schedule 10.12.2011


Ответы (3)


Нет такой вещи, как символ, который существует в UTF-16, но не существует в UTF-8. Оба способны кодировать весь Юникод. Другими словами, если вы можете заставить работать UTF-8, он сможет хранить любой действительный текст Unicode.

РЕДАКТИРОВАТЬ: суррогатные пары на самом деле являются функцией UTF-16, а не чем UTF-8. Они позволяют представить символ, не входящий в базовую многоязычную плоскость (BMP), в виде двух кодовых единиц UTF-16. По сути, UTF-16 часто рассматривается как кодировка с фиксированной шириной (ровно два байта на символ Unicode), но это позволяет чисто кодировать только BMP. Суррогатные пары - это (довольно хакерский) способ расширить диапазон за пределы BMP.

Я очень сомневаюсь, что персонаж, которого вы пытаетесь представить, находится за пределами BMP, поэтому я подозреваю, что вам нужно искать проблему в другом месте. В частности, стоит сбросить точные значения символов текста (например, путем приведения каждого char к int) до того, как он попадет в базу данных, и после того, как вы его извлечете. В идеале сделайте это в коротком, но полноценном консольном приложении.

person Jon Skeet    schedule 09.12.2011
comment
Мои познания в этом невелики, поэтому похоже, что я ошибаюсь - я обновлю вопрос и удалю свое предположение. - person Mr Shoubs; 09.12.2011
comment
Спасибо, Джон, вы указали мне правильное направление, это может быть связано с используемой нами версией Postgres или нашим программным обеспечением для кластеризации. Я написал полностью локальный тест, и у меня не было той проблемы, которая существует в нашей производственной системе. - person Mr Shoubs; 09.12.2011
comment
@MrShoubs: Это может означать, что проблема не там, где вы думаете, - можете ли вы запустить свою тестовую программу на своей производственной системе, чтобы убедиться, что она там не работает? (Рад, что совет помогает.) - person Jon Skeet; 09.12.2011

Как я могу хранить все «символы» UTF-16 в базе данных Postgres?

Короткий ответ: это невозможно напрямую, поскольку PostgreSQL поддерживает только набор символов UTF-8.

Форматы на основе UTF-16, такие как Java, JavaScript, Windows, могут содержать полусуррогатные пары, которые не представлены в UTF-8 или UTF-32. Их можно легко создать путем подстроки строки Java, JavaScript, VB.Net. Поскольку они не могут быть представлены в UTF-8 или UTF-32 и, следовательно, не могут храниться в базе данных, которая поддерживает только набор символов UTF-8, например PostgreSQL.

Имена путей Windows могут содержать половину суррогатных пар, которые не могут быть прочитаны как utf-8 ( https://github.com/rust-lang/rust/issues/12056).

Придется использовать систему баз данных, которая поддерживает набор символов UTF-16/CESU-8, который более адаптирован к языкам/платформам Java/Android, JavaScript/NodeJS, .Net/wchar_t/Windows. (SQLServer, Oracle (сопоставление UTF-8), DB2, Informix, HANA, SQL Anywhere, MaxDB обычно поддерживают такую ​​кодировку.

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

В postgres вы можете: а) принять потери, б) сохранить данные как двоичные данные или в) преобразовать их в закодированное представление (например, JSON rfc кодирует их как два экранированных символа, чтобы иметь возможность передавать половину суррогатов в UTF- 8/Сетевой формат на основе Ascii без потерь (https://tools.ietf.org/html/rfc4627 Раздел 2.5).

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

В зависимости от выбора языка Application Server (Java, Scala, C#/Windows, JavaScript/NodeJS) и уровня инвестиций в языковую поддержку (например, с использованием функций разделения строк ICU на границах графемы (https:/)/www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) вместо простого усечения проблема может быть менее актуально, но большинство корпоративных систем и языков сегодня относятся к лагерю UTF-16, а программное обеспечение использует простые операции с подстроками.

person user6649841    schedule 15.12.2018

Что касается проблемы хранения/восстановления č

  1. Убедитесь, что в базе данных Postgre работает набор символов UTF-8 (https://www.postgresql.org/docs/9.1/multibyte.html ) или набор символов, который может представлять символ.

  2. Убедитесь, что клиентское соединение с базой данных настроено для выполнения соответствующего преобразования кодовой страницы (для VB.Net это будет из UTF-16LE в UTF-8 или кодировку базы данных, обычно это параметр в строке подключения (кодировка) ).

  3. Убедитесь, что ввод является фактической последовательностью байтов UTF-8/UTF-16 в VB.net, а не последовательностью байтов Windows-1250.

  4. Убедитесь, что это не просто ограничение средства вывода или консоли (например, консоль Windows обычно не отображает символы Юникода, а использует набор символов Windows-12xx (можно попробовать https://superuser.com/questions/269818/change-default-code-page-of-windows-console-to-utf-8), но обычно лучше всего проверять последовательность байтов в отладчике VB.Net.

  5. Убедитесь, что длина столбца CHAR/VARCHAR достаточна для хранения вашего представления, даже если оно представлено в декомпозиции NFKD.

Указанная вами графема имеет несколько различных представлений Юникода.

 U+010D LATIN SMALL LETTER C WITH CARON
 U+0063 LATIN SMALL LETTER c followed by U+030C COMBINING CARON

И другие представления других наборов символов (например, 0xE8 в ISO-8859-2/Windows-1250 (https://en.wikipedia.org/wiki/Windows-1250) или ISO-8859-13/Windows-1257.

Все представления Unicode попадают в базовую многоязычную плоскость, поэтому суррогатная проблема UTF-16 с postgre, как указано в заголовке вопроса и ответ ниже, вероятно, не имеет отношения к вашей проблеме.

person user6649841    schedule 15.12.2018