Многобайтовая (китайская) кодировка символов PDO DBLIB — SQL-сервер

На машине с Linux я использую PDO DBLIB для подключения к базе данных MSSQL и вставки данных в таблицу SQL_Latin1_General_CP1_CI_AS. Проблема в том, что когда я пытаюсь вставить китайские символы (многобайтовые), они вставляются как 哈市香åŠåŒºç æ±Ÿè·¯å·.

Мой (часть) код выглядит следующим образом:

$DBH = new PDO("dblib:host=$myServer;dbname=$myDB;", $myUser, $myPass);

$query = "
    INSERT INTO UserSignUpInfo
    (FirstName)
    VALUES
    (:firstname)";

$STH = $DBH->prepare($query);

$STH->bindParam(':firstname', $firstname);

Что я пробовал до сих пор:

  1. Выполнение mb_convert_encoding до UTF-16LE на $firstname и CAST как VARBINARY в запросе, например:

    $firstname = mb_convert_encoding($firstname, 'UTF-16LE', 'UTF-8');

    VALUES
    (CAST(:firstname AS VARBINARY));
    

    Это приводит к правильной вставке символов, пока не появятся некоторые не многобайтовые символы, которые прерывают выполнение PDO.

  2. Установка моего соединения как utf8:

    $DBH = new PDO("dblib:host=$myServer;dbname=$myDB;charset=UTF-8;", $myUser, $myPass);
    $DBH->exec('SET CHARACTER SET utf8');
    $DBH->query("SET NAMES utf8");
    
  3. Установка client charset в UTF-8 в моем файле freetds.conf

    Что никак не повлияло.

Есть ли вообще способ вставить многобайтовые данные в эту базу данных SQL? Есть ли другой обходной путь? Я думал попробовать PDO ODBC или даже mssql, но подумал, что лучше спросить здесь, прежде чем тратить время.

Заранее спасибо.

ИЗМЕНИТЬ:

В итоге я использовал MSSQL и префикс типа данных N. Я заменю и попробую PDO_ODBC, когда у меня будет больше времени. Спасибо всем за ответы!


person Manolis    schedule 26.02.2015    source источник
comment
Извините за мой вопрос, но иногда сопоставление поля отличается от сопоставления таблицы, поэтому я хочу знать тип и сопоставление вашего поля; плз.   -  person shA.t    schedule 09.03.2015
comment
Вы тестируете nvarchar() или ntext?   -  person shA.t    schedule 10.03.2015
comment
В соответствии с этим вы можете сначала преобразовать данные, а затем вставить . Ссылка делает что-то вроде этого: $STH->bindValue(':value', iconv('UTF-8', 'ISO8859-1', $value));. Это похоже на первый пункт вашего вопроса в разделе Что я пробовал до сих пор. Я не понимаю, в какой тип столбца вы вставляете, так что это может не сработать.   -  person chue x    schedule 16.04.2015
comment
@chuex Я попробовал это решение и могу убедиться, что оно, к сожалению, не работает.   -  person Drakes    schedule 17.04.2015


Ответы (5)


Есть ли вообще способ вставить многобайтовые данные в [эту конкретную] базу данных SQL? Есть ли другой обходной путь?

  1. Если вы можете переключиться на PDO_ODBC, Microsoft предоставляет бесплатные драйверы ODBC для SQL Server для Linux (только для 64-разрядной версии Red Hat Enterprise Linux и 64-разрядной версии SUSE Linux Enterprise), которые поддерживают Unicode.

  2. Если вы можете перейти на PDO_ODBC, то N-префикс для вставки Unicode будет работать.

  3. Если вы можете изменить затронутую таблицу с SQL_Latin1_General_CP1_CI_AS на UTF-8 (что по умолчанию для MSSQL), то это было бы идеально.

Ваш случай более ограничен. Это решение подходит для случая, когда у вас во входной строке смешаны многобайтовые и не многобайтовые символы, и вам нужно сохранить их в латинскую таблицу, а префикс типа данных N не работает, и вы не хотите чтобы отказаться от PDO DBLIB (поскольку Microsoft Unicode PDO_ODBC едва поддерживается в Linux). Вот один обходной путь.

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

Рабочий пример:

$DBH = new PDO("dblib:host=$myServer;dbname=$myDB;", $myUser, $myPass);

$query = "
INSERT INTO [StackOverflow].[dbo].[UserSignUpInfo]
           ([FirstName])
     VALUES
           (:firstname)";

$STH = $DBH->prepare($query);

$firstname = "输入中国文字!Okay!";

/* First, check if this string has any Unicode at all */
if (strlen($firstname) != strlen(utf8_decode($firstname))) {
    /* If so, change the string to base64. */
    $firstname = base64_encode($firstname);
}

$STH->bindParam(':firstname', $firstname);
$STH->execute(); 

Затем, чтобы вернуться назад, вы можете проверить строки base64 и декодировать только их, не повреждая существующие записи, например:

while ($row = $STH->fetch()) {
    $entry = $row[0];

    if (base64_encode(base64_decode($entry , true)) === $entry) {

         /* Decoding and re-encoding a true base64 string results in the original entry */
         print_r(base64_decode($entry) . PHP_EOL);

    } else {

         /* Previous entries not encoded will fall through gracefully */
         print_r($entry  . PHP_EOL);
    }
}

Записи будут сохранены следующим образом:

Guan Tianlang
5pys6Kqe44KS5a2maGVsbG8=

Но вы можете легко преобразовать их обратно в:

Guan Tianlang
输入中国文字!Okay!
person Drakes    schedule 17.04.2015
comment
Большое спасибо за ваши усилия по написанию этого ответа! К сожалению, система, которая извлекает данные после их сохранения в БД, находится на .NET, и у меня нет доступа. Тем не менее, это приятный обходной путь. - person Manolis; 22.04.2015

Сопоставление не должно иметь значения здесь.

Двухбайтовые символы необходимо хранить в полях nvarchar, nchar или ntext. Вам не нужно выполнять какой-либо кастинг.

Префикс типа данных n означает национальный, и он заставляет SQL Server хранить текст в формате Unicode (UTF-16).

Изменить:

PDO_DBLIB не поддерживает Unicode и теперь считается устаревшим.

Если вы можете переключиться на PDO_ODBC, Microsoft предоставляет бесплатные драйверы ODBC для SQL Server для Linux, поддерживающие Unicode.

Microsoft — документация по драйверу ODBC для SQL Server

Блог — Установка и использование драйвера ODBC Microsoft SQL Server для Linux

person Jon Tirjan    schedule 10.04.2015
comment
Только что попробовал использовать mssql_query с PHP и N'', и все заработало. Интересно, есть ли способ заставить PDO_DBLIB работать так... - person Manolis; 10.04.2015
comment
В этой статье на php.net говорится, что PDO_DBLIB устарел, и рекомендуется использовать SqlSrv в Windows и PDO_ODBC в других местах. php.net/manual/en/ref.pdo-dblib.php. Можете ли вы попробовать PDO_ODBC? - person Jon Tirjan; 10.04.2015
comment
Хотя это может быть устаревшим, на самом деле это не помогает OP с PDO_DBLIB. Сначала он ищет обходной путь. - person Drakes; 18.04.2015
comment
Не уверен, что согласен. ОП фактически упомянул об использовании альтернативных драйверов. Я думаю, что гораздо лучше использовать драйверы, которые изначально поддерживают то, что нужно OP, чем пытаться взломать что-то вместе с устаревшим драйвером, который больше не поддерживается. - person Jon Tirjan; 18.04.2015
comment
Драйвер MS предназначен для 64-разрядной версии Red Hat Enterprise Linux и 64-разрядной версии SUSE Linux Enterprise. Ему придется менять всю коробку! :) - person Drakes; 18.04.2015
comment
Лол, поменять ему всю коробку с чего именно? Он не уточнил, но Red Hat и SUSE доминируют на рынке серверов Linux. С Fedora проблем быть не должно, и вот статья об успешной установке Ubuntu. - person Jon Tirjan; 18.04.2015
comment
Ваше решение будет самым разумным, если он сможет все настроить. Я согласен, что обходные пути не идеальны, но было забавно придумать это решение. Первоначально я проголосовал за этот ответ, но сейчас я это отменил. Извинения. Кажется, за каждый ответ проголосовали отрицательно... кроме ответа Имрана. Подозрительный. - person Drakes; 18.04.2015
comment
PDO_DBLIB устарел в Windows, в Linux его все еще можно рассмотреть - person MaxiWheat; 18.02.2016

Вы можете использовать совместимый с Unicode тип данных для столбца таблицы для поддержки иностранных языков (исключения показаны в РЕДАКТИРОВАТЬ 2).

(char, varchar, text) В сравнении (nchar, nvarchar, ntext)

Не-Юникод:

Лучше всего подходит для английского языка США: «Одна проблема с типами данных, которые используют 1 байт для кодирования каждого символа, заключается в том, что тип данных может представлять только 256 различных символов. Это требует использования нескольких спецификаций кодирования (или кодовых страниц) для разных алфавитов, таких как европейские которые относительно малы. Также невозможно работать с такими системами, как японский кандзи или корейский алфавит хангыль, которые содержат тысячи символов.

Юникод

Лучше всего подходит для систем, которые должны поддерживать хотя бы один иностранный язык: «Спецификация Unicode определяет единую схему кодирования для большинства символов, широко используемых в бизнесе по всему миру. Все компьютеры последовательно переводят битовые комбинации в данных Unicode в символы, используя единый Unicode Это гарантирует, что одна и та же битовая комбинация всегда преобразуется в один и тот же символ на всех компьютерах.Данные могут свободно передаваться из одной базы данных или компьютера в другую, не беспокоясь о том, что принимающая система неправильно преобразует битовые комбинации в символы.

Пример :

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

введите здесь описание изображения

РЕДАКТИРОВАТЬ 1:

Другая связанная проблема обсуждается здесь

РЕДАКТИРОВАТЬ 2:

Скрипты, не поддерживаемые Unicode, показаны здесь

person Tharif    schedule 17.04.2015

просто используйте nvarchar, ntext, nChar, и когда вы хотите вставить, используйте

INSERT INTO UserSignUpInfo
    (FirstName)
    VALUES
    (N'firstname');

N будет относиться к символу Unicode, и это стандарт во всем мире.

Ссылка:

https://aalamrangi.wordpress.com/2012/05/13/storing-and-retrieving-non-english-unicode-characters-hindi-czech-arabic-etc-in-sql-server/

https://technet.microsoft.com/en-us/library/ms191200(v=sql.105).aspx

https://irfansworld.wordpress.com/2011/01/25/what-is-unicode-and-non-unicode-data-formats/

person Imran Ali Khan    schedule 17.04.2015
comment
ОП сказал, что только что попробовал использовать mssql_query с PHP и N '', и все сработало нормально. Интересно, есть ли способ заставить PDO_DBLIB работать так. Пожалуйста, внимательно прочитайте другие его комментарии. - person Drakes; 18.04.2015

Эта ссылка Объясните китайский иероглиф в MYSQL. Не удается вставить китайский символ в MySQL . Вы должны создать таблицу table_name () CHARACTER SET = utf8; Use UTF-8 при вставке в таблицу

set username utf8; INSERT INTO table_name (ABC,VAL); 

abd создать базу данных в CHARACTER SET utf8 COLLATE utf8_general_ci;

то вы можете вставить китайский иероглиф в таблицу

person ABIRAMAN    schedule 06.03.2015
comment
Таблица SQL_Latin1_General_CP1_CI_AS и, к сожалению, изменить ее нельзя - это часть огромной корпоративной системы. - person Manolis; 09.03.2015
comment
mysql_query("SET character_set_client=utf8", $dbLink)or die(mysql_error()); mysql_query("SET character_set_connection=utf8", $dbLink)or die(mysql_error()); попробуй так - person ABIRAMAN; 09.03.2015