PDO DBLIB多字节(中文)字符编码 - SQL服务器

Man*_*lis 16 sql-server pdo character-encoding freetds multibyte

在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);
Run Code Online (Sandbox Code Playgroud)

到目前为止我尝试过的:

  1. 这样做mb_convert_encodingUTF-16LE$firstname和CAST作为VARBINARY像查询:

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

    VALUES
    (CAST(:firstname AS VARBINARY));
    
    Run Code Online (Sandbox Code Playgroud)

    这导致正确插入字符,直到有一些非多字节字符,这会破坏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");
    
    Run Code Online (Sandbox Code Playgroud)
  3. client charset在我的freetds.conf中设置为UTF-8

    哪个没有影响.

有没有办法在该SQL数据库中插入多字节数据?还有其他解决方法吗?我曾经想过尝试PDO ODBC甚至是mssql,但是在浪费时间之前最好先问一下这个问题.

提前致谢.

编辑:

我最终使用MSSQLN数据类型前缀.当我有更多时间时,我会换掉并尝试PDO_ODBC.谢谢大家的答案!

Dra*_*kes 8

有什么办法可以在[这个特定的] SQL数据库中插入多字节数据吗?还有其他解决方法吗?

  1. 如果您可以切换到PDO_ODBC,Microsoft为Linux提供免费的SQL Server ODBC驱动程序(仅适用于支持Unicode的64位Red Hat Enterprise Linux和64位SUSE Linux Enterprise).

  2. 如果您可以更改为PDO_ODBC,则插入Unicode的N前缀将起作用.

  3. 如果您可以将受影响的表更改SQL_Latin1_General_CP1_CI_ASUTF-8(这是MSSQL的默认值),那么这将是理想的.

你的案子受到更多限制.此解决方案适用于输入字符串中包含混合多字节和非多字节字符的情况,并且需要将它们保存到拉丁表,并且N数据类型前缀不起作用,并且您不希望改变远离PDO DBLIB(因为Linux上几乎不支持 Microsoft的Unicode PDO_ODBC ).这是一个解决方法.

有条件地将输入字符串编码为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(); 
Run Code Online (Sandbox Code Playgroud)

然后向后,您可以测试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);
    }
}
Run Code Online (Sandbox Code Playgroud)

参赛作品将保存如下:

Guan Tianlang
5pys6Kqe44KS5a2maGVsbG8=
Run Code Online (Sandbox Code Playgroud)

但您可以轻松地将它们转换回:

Guan Tianlang
???????Okay!
Run Code Online (Sandbox Code Playgroud)