从一个数据库读取数据并使用PHP插入另一个数据库时出现编码错误

时间:2014-07-03 07:19:15

标签: php postgresql utf-8 character-encoding pervasive

使用PHP,我试图从Pervasive DB v9.5读取数据并将其插入到Windows 2008上的PostgreSQL 9.3(编码:UTF-8)。我没有选择或编写PervasiveDB(我只是从中读取数据) )。使用ODBC,我从Pervasive读取数据并将其写入控制台,没有任何问题。但是,当我尝试将其插入Postgre时遇到

Warning: pg_execute(): Query failed: ERROR:  invalid byte sequence for encoding "UTF8": 0x94 in file.php on line ..

所以,我看到Postgres不喜欢我给的字符串。

然后我用

var_dump(iconv_get_encoding('all'));

并看到我的编码是ISO-8859-1

并使用

修改字符串
iconv ( 'ISO-8859-1' , 'UTF-8' , $a)

现在,错误消失了。然而,到达Postgres的字符串不正确。

我使用的代码如下。我的测试字符串是aöaçaşaıağaüaÖaÇaŞaİaĞaÜ

$ a是来自Pervasive的字符串

echo $a; 

给aöaçaşaıağaüaÖaÇaŞaİaĞaÜ

echo iconv ( 'ISO-8859-1' , 'UTF-8' , $a)

给a┬öa┬ça┬şa┬ıa┬ğa┬üa┬Öa┬Ça┬Şa┬İa┬Ğa┬Ü

<?php
//var_dump(iconv_get_encoding('all'));

$conn = pg_connect("host=localhost port=5432 dbname=xxx user=xxx password=".$argv[1]);

$result = pg_prepare($conn, "my_query", 'SELECT * FROM func_my_deneme($1)');

$connect_string = "DRIVER={Pervasive ODBC Client Interface}; SERVERNAME=localhost; SERVERDSN=xxx;";
$pervasiveconn = odbc_connect($connect_string, 'xxx', 'xxx');

$pervasive_result = odbc_exec($pervasiveconn ,"SELECT something");

while(odbc_fetch_row($pervasive_result)){
  $a=odbc_result($pervasive_result,1);

  echo $a;

  $result = pg_execute($conn, "my_query", array(iconv ( 'ISO-8859-1' , 'UTF-8' , $a)));
}
?>

1 个答案:

答案 0 :(得分:3)

你似乎只是在看这两个编码交换中的一个。

你有:

(pervasive's native encoding) -> (PHP string)

(PHP string) -> (PostgreSQL)

其中,您只是明确处理第二个问题。您假设Pervasive的ODBC驱动程序返回的数据是PHP的默认编码,在您的系统上是iso-8859-1。

您的测试表明假设可能是正确的,但简单地回显字符串并不是一个好的方法,因为这引入了另一个编码步骤:

(PHP string) -> (whatever decodes it for viewing)

是一个网络浏览器,终端或其他什么。如果观众希望某些编码与Pervasive使用的编码相同,则会对输出进行核心解码。

尝试:

echo $a;
echo "aöaçaşaıağaüaÖaÇaŞaİaĞaÜ";

并确保查看器显示两者的相同值。确保编辑源文件的编码设置为iso-8859-1,而不是其他编码,以便粘贴的字符串的字面字节正确。

此时如果您的编辑器设置正确,您应该会收到错误,因为并非所有这些字符在iso-8859-1中都是合法的。第一个无效的是ş

很明显,来自Pervasive的东西不能是iso-8859-1。要真正打印latin-1字符串,您可以回显转义的字节。例如,这个字符串:

aöaçaaaüaÖaÇaaaaÜ

其中所有字符都是合法的iso-8859-1,以iso-8859-1编码打印:

echo "a\xf6a\xe7aaa\xfca\xd6a\xc7aaaa\xdc"

这里,十六进制转义用于指定非7位字符,以明确确保字节序列的编码符合您的想法,而不会对文本编辑器等造成任何混淆。

Betcha在您查看时无法正确打印,因为无论读取什么输入都不能将其解码为iso-8859-1。


你应该做的是查看从Pervasive获得的字符串的 bytes ,看看它到底是什么。然后确定其编码并将其解码为utf-8,然后您可以通过client_encoding = utf-8连接将其发送到PostgreSQL。 @deceze为此建议bin2hex(我不会说PHP,所以不知道该建议什么)。所以显示输出:

echo bin2hex($a) . "\n";

或 - 甚至更好 - 确保从配置/文档中确定来自Pervasive的数据的编码是什么,而不是猜测。或者强迫它。

快速浏览一下Pervasive文档,显示ODBC驱动程序有一个encoding参数,该参数获取所需编码的代码页ID。所以试试:

$connect_string = "DRIVER={Pervasive ODBC Client Interface}; SERVERNAME=localhost; SERVERDSN=xxx; encoding=65001";

(微软至少将65001定义为每this doc个utf-8的代码页。)