2017-01-30 18 views
7

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

У меня есть веб-приложение на PHP, которое вводит пользователя. Из-за характера приложения пользователи могут часто использовать расширенные символы ASCII (a.k.a. «ALT-коды»).

Моя конкретная проблема на данный момент - это код ALT 26, который является стрелкой вправо (→). Это будет сопровождаться другим текстом, который будет храниться в одном поле (например, 'this→that').

Мой тип колонки - NVARCHAR.

Вот что я пробовал:

  1. Я пытался не делать никаких преобразований и просто вставить значение в обычном режиме, но значение получает хранится в thisâ??that.

  2. Я попытался преобразовать значение в UCS-2 в PHP, используя iconv('UTF-8', 'UCS-2', $value), но я получаю сообщение об ошибке Unclosed quotation mark after the character string 't'.. Запрос заканчивается следующим образом: UPDATE myTable SET myColumn = 'this�!that'.

  3. Я попытался выполнить вышеуказанное преобразование, а затем добавил N до цитируемого значения, но получаю то же сообщение об ошибке. Запрос выглядит так: UPDATE myTable SET myColumn = N'this�!that'.

  4. Я попытался удалить конверсию UCS-2 и просто добавить N перед цитируемым значением, и запрос снова работает, но значение сохраняется как thisâ that.

  5. Я попытался использовать utf8_decode($value) в PHP, но затем стрелка только что заменена вопросительным знаком.

Так может кто-нибудь ответить на (казалось бы, простой) вопрос о том, как я могу хранить это значение в моей базе данных, а затем восстановить его, как он был набран?

Я использую PHP 5.5 и MSSQL 2012. Если какой-либо вопрос о версии драйвера/ОС входит в игру, это сервер Linux, подключающийся через FreeTDS. Нет возможности изменить это.

+1

Уверены, что значение, хранящееся на сервере sql, неверно? SSMS не всегда делает хорошую работу по отображению символов в расширенном наборе. Вы можете проверить фактическое значение unicode этого символа. Похоже, что есть что-то на стороне PHP, которую вы должны сделать, чтобы сделать это действительным для расширенного набора символов. –

+0

Чтобы ответить на это: «Я пробовал использовать utf8_decode ($ value) в PHP, но затем стрелка просто заменяется вопросительным знаком.», Это, вероятно, потому, что нет представления для этого значения в ISO-8859-1 кодировок. utf8_decode преобразует строки в кодировку ISO-8859-1. – georaldc

+0

@SeanLange: Когда значение извлекается из базы данных для отображения в приложении, оно отображается, как я упоминал (сломан). – Travesty3

ответ

5

Вы можете попробовать кодирование base64 вход, это достаточно тривиально обращаться с РНР base64_encode() и base64_decode() и он должен обрабатывать то, что когда-либо ваши пользователи бросали на него.

(изменить: Возможно, вы также можете сделать base64 encoding on the SQL Server side. Это не похоже на то, что он должен отвечать за imho, но это вариант.)

+0

Хорошая идея! Я просто проверил это и, похоже, работает так, как ожидалось. Вид обломка, который я должен добавить, чтобы эти шаги выполнялись до и после его хранения, но это единственное, что до меня дошло до меня. Благодаря! FYI, он не даст мне награду за 16 часов. Я думаю, у других будет 16 часов, чтобы придумать лучшее решение! – Travesty3

1

Похоже, что ваш freetds.conf неверен. Для поддержки Unicode вам нужна версия протокола TDS> = 7.0. See this for more details.

редактировать свои freetds.conf:

[global] 
# TDS protocol version 
tds version = 7.4 
client charset = UTF-8 

Также убедитесь, что настройки PHP правильно:

ini_set('mssql.charset', 'UTF-8'); 
+0

Ну, 'tds version' _should_ будет' 7.4', поскольку OP обращается к экземпляру SQL Server 2012. В документе, на который вы ссылаетесь, говорится: «Для достижения наилучших результатов используйте самую высокую версию протокола, поддерживаемого вашим сервером». –

+0

@LayZee по той же ссылке: * 4.2 Все еще работает со всеми продуктами с учетом его ограничений. * ASCI вместо UTF-8 является ограничением. Просто использование нового продукта не предполагает, что вы не можете использовать более старый протокол. –

+0

Согласен. Я просто хотел указать, что 7.4 возможно и даже рекомендовано в документах. –

1

Как у вас есть this→!that, ASCII-копия кодировки UTF-8 this→!that.

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

Вам нужно начать новый поиск, чтобы найти правильный путь.

  • Создать новую таблицу, по крайней мере один столбец NVARCHAR, скажем TestTable(Column1)
  • Создание пустого UTF-8 закодированный -Обязательные для жестко закодированных входов во tests- PHP файл. test.php
  • В freetds.conf обязательно добавьте настройку client charset = UTF-8 под номером [global] или [WhateverYourServerNameIs]. Это набор символов, который будет использоваться при обмене данными между FreeTDS и SQL Server.

test.php:

<?php 
// character set to be used in response 
header('Content-Type: text/plain; charset=utf-8'); 

// charset to be used in communication between PHP driver and FreeTDS. 
ini_set('mssql.charset', 'UTF-8'); 

// make the connection 
$conn = mssql_connect("ServerNameSpecifiedInFreetdsDotConf", "username", "passw0rd"); 

// select database 
mssql_select_db("DatabaseName", $conn); 

// insert something immediately 
// do not forget to use the N prefix, may cause problems with mssql_query if omitted 
$insertQuery = mssql_query("Insert Into TestTable(Column1) Select N'this→that';", $conn); 

// list all the records 
$query = mssql_query("Select * From TestTable", $conn); 
while ($row = mssql_fetch_array($query)) 
    echo $row["Column1"].PHP_EOL; 

mssql_close($conn); // close the connection 
?> 

Run test.php несколько раз и посмотреть, что происходит. Если вы видите this→that как для ответа PHP, так и для SQL Server Management Studio, это означает, что вы все настроены.

Если вы все еще получаете неожиданные результаты, сообщите нам об этом.

+0

В моих примерах я активно передаю 'this → that' в базу данных для каждого теста. Я знаю, что если он некорректно хранится перед моими изменениями, они не будут исправлены путем изменения настроек. Ничего страшного.Я попробовал установить 'client charset = UTF-8' в моем freetds.conf, я убедился, что я устанавливаю заголовок' charset = utf-8' и стараюсь использовать префикс N в моем запросе, но все равно не нужно помогло. Значение по-прежнему сохраняется как 'thisâ that' и возвращается в мое приложение таким же образом. Возможно, стоит отметить, что я не использую функции vanilla 'mssql_ *', я использую Zend Framework 2, который использует PDO. – Travesty3

+0

@ Travesty3 Я вижу. Это большой пробел, не упоминающий Zend и PDO. Если вы хотите получить решение, а не обходной путь, пожалуйста, покажите нам свой код, чтобы мы могли воспроизвести проблему, было бы очень полезно. –

+1

К сожалению, здесь слишком много настроек рамки, чтобы вставить весь соответствующий код здесь. Я надеялся, что существует более общепринятое решение или будет показано, что текст запроса будет сгенерирован. Тем не менее, ваше стремление к решению очень ценится! – Travesty3

1

Принятый ответ, похоже, выполняет эту работу; да, вы можете закодировать его до base64, а затем снова декодировать его, но затем все приложения, использующие эту удаленную базу данных, должны изменить и поддерживать поля, которые должны быть закодированы base64. Я считаю, что если есть удаленная база данных MS SQL Server, может быть другое приложение (или приложения), которое может его использовать, так что приложение также должно быть изменено для поддержки как простой, так и base64 кодировки. И вам также придется обрабатывать как обычный текст, так и преобразованный текст base64.

Я немного искал, и я нашел, как отправить текст UNICODE на MS SQL Server с помощью команд MS SQL и PHP для преобразования байтов UNICODE в номера HEX.

Если вы идете в PHP документации для mssql_fetch_array (http://php.net/manual/ru/function.mssql-fetch-array.php#80076), вы увидите на комментариях довольно хорошее решение, которое преобразует текст в значение шестнадцатеричного, а затем посылает эти данные HEX непосредственно в MS SQL Server, как это :

Преобразование текста Unicode в HEX данных

// sending data to database 
$utf8 = 'Δοκιμή με unicode → Test with Unicode'; // some Greek text for example 
$ucs2 = iconv('UTF-8', 'UCS-2LE', $utf8); 

// converting UCS-2 string into "binary" hexadecimal form 
$arr = unpack('H*hex', $ucs2); 
$hex = "0x{$arr['hex']}"; 

// IMPORTANT! 
// please note that value must be passed without apostrophes 
// it should be "... values(0xABCEF) ...", not "... values('0xABCEF') ..." 
mssql_query("INSERT INTO mytable (myfield) VALUES ({$hex})", $link); 

Теперь весь текст на самом деле хранится в поле NVARCHAR базы данных правильно, как UNICODE, и это все, что вам нужно сделать для того, чтобы отправить и сохранить его как plai n текст и не закодирован.

Чтобы получить этот текст, вы должны спросить MS SQL Server для отправки обратно Юникоде текст следующим образом:

Получение Unicode текст из MS SQL Server

// retrieving data from database 
// IMPORTANT! 
// please note that "varbinary" expects number of bytes 
// in this example it must be 200 (bytes), while size of field is 100 (UCS-2 chars) 

// myfield is of 50 length, so I set VARBINARY to 100 
$result = mssql_query("SELECT CONVERT(VARBINARY(100), myfield) AS myfield FROM mytable", $link); 

while (($row = mssql_fetch_array($result, MSSQL_BOTH))) 
{ 
    // we get data in UCS-2 
    // I use UTF-8 in my project, so I encode it back 
    echo '1. '.iconv('UCS-2LE', 'UTF-8', $row['myfield'])).PHP_EOL; 
    // or you can even use mb_convert_encoding to convert from UCS-2LE to UTF-8 
    echo '2. '.mb_convert_encoding($row['myfield'], 'UTF-8', 'UCS-2LE').PHP_EOL; 
} 

МС таблицы SQL с данными UNICODE после INSERT

MS SQL Table

44 +44516410617451515053691368888

Выходной результат с помощью PHP страницу для отображения значений

PHP Output

Я не уверен, если вы можете добраться до моей тестовой страницы, но вы можете попробовать, чтобы увидеть живые результаты: http://dbg.deve.wiznet.gr/php56/mssql/test1.php