sql-server-freebcp:“Unicode数据是列的奇数字节大小.应该是偶数字节大小“

前端之家收集整理的这篇文章主要介绍了sql-server-freebcp:“Unicode数据是列的奇数字节大小.应该是偶数字节大小“前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
文件工作正常(UTF-8):
$cat ok.txt
291054  Ţawī Rifā

文件导致错误(UTF-8):

$cat bad.txt
291054  Ţawī Rifā‘

这是消息:

$freebcp 'DB.dbo.table' in bad.txt ... -c
Starting copy...
Msg 20050,Level 4
Attempt to convert data stopped by Syntax error in source field

Msg 4895,Level 16,State 2
Server '...',Line 1
    Unicode data is odd byte size for column 2. Should be even byte size.
Msg 20018,Level 16
General sql Server error: Check messages from the sql Server

唯一的区别是最后一个字符,它是unicode 2018(左单引号)

任何想法是什么导致这个错误

sql Server使用UTF-16LE(尽管TDS以UCS-2LE启动并切换了我的看法)

该列是nvarchar(200)

这是在错误之前发送的数据包:

packet.c:741:Sending packet
0000 07 01 00 56 00 00 01 00-81 02 00 00 00 00 00 08 |...V.... ........|
0010 00 38 09 67 00 65 00 6f-00 6e 00 61 00 6d 00 65 |.8.g.e.o .n.a.m.e|
0020 00 69 00 64 00 00 00 00-00 09 00 e7 90 01 09 04 |.i.d.... ...ç....|
0030 d0 00 34 04 6e 00 61 00-6d 00 65 00 d1 ee 70 04 |Ð.4.n.a. m.e.Ñîp.|
0040 00 13 00 62 01 61 00 77-00 2b 01 20 00 52 00 69 |...b.a.w .+. .R.i|
0050 00 66 00 01 01 18      -                        |.f....|

解决方法

由于传输数据包(问题底部)所示的数据是UTF-16 Little Endian(正如sql Server所期望的),UTF-8与UTF-8无关.而且这是非常好的UTF-16LE,除了丢失的最后一个字节,就像错误信息所暗示的一样.

问题很可能是一个小错误,错误地应用逻辑意图从可变长度字符串字段中删除尾随空格.没有尾随空格,你说?那么如果没有被砍掉,那么会更清楚一点(但是如果没有被砍掉就不会有这个错误).所以,让我们来看看我们可以重构的数据包.

数据中的错误可能被忽略,因为数据包包含偶数个字节.但是并不是所有的字段都是双字节的,所以它不需要是一个偶数.如果我们知道好的数据是什么(在错误之前),那么我们可以在数据中找到一个起点,向前移动.最好从Ţ开始,因为它希望高于255 / FF值,因此需要2个字节.下面的任何一个都会有一个00,许多字符都有这两个.虽然我们应该能够假设小端编码,但最好是知道一些.为此,我们需要至少一个字符,它具有两个非00字节,并且字节不同(两个字节的字符之一为01,并且不帮助确定排序).该字符串字段的第一个字符Ţ确认,因为它是Code Point 0162,而是在数据包中显示为62 01.

以下是与数据包相同的字符,它们的UTF-16 LE值,以及指向其全部细节的链接. 62 01的第一个字符的字节序列给出了我们的起点,所以我们可以忽略第0040行的第00 00 00号(它们在下面的副本中被删除,以便于读取).请注意,右侧所示的“翻译”不会解释Unicode,因此62 01的2字节序列本身(即小写拉丁语“b”)和01本身显示为62(即不可打印)字符;显示为“.”).

0040 xx xx xx 62 01 61 00 77-00 2b 01 20 00 52 00 69 |...b.a.w .+. .R.i|  
0050 00 66 00 01 01 18 ??   -                        |.f....|

>Ţ – 62 01 – http://unicode-table.com/en/0162/
> a – 61 00 – http://unicode-table.com/en/0061/
> w – 77 00 – http://unicode-table.com/en/0077/
>ī – 2B 01 – http://unicode-table.com/en/012B/
> – 20 00 – http://unicode-table.com/en/0020/
> R – 52 00 – http://unicode-table.com/en/0052/
>我 – 69 00 – http://unicode-table.com/en/0069/
> f – 66 00 – http://unicode-table.com/en/0066/
>ā – 01 01 – http://unicode-table.com/en/0101/
” – 18 20 – http://unicode-table.com/en/2018/

正如你所看到的,最后一个字符真的是18 20(即由于Little Endian编码而是字节交换20 18),而不是01 18,如果从最后开始读取数据包可能会出现.不知何故,最后一个字节 – 十六进制20 – 缺少,因此Unicode数据是奇数字节大小错误.

现在,20个本身,或者跟着00,是一个空间.这将解释为什么@GordThompson能够通过添加一个额外的字符来获得它的工作(最后的字符不再可调整).这可以通过以U 20xx代码点的另一个字符结束来进一步证明.例如,如果我是正确的,那么结束于/ – Fraction Slash U+2044 – 会有同样的错误,而以⅄ – Turned Sans-Serif Capital Y U+2144结尾 – 即使在“之前,应该工作很好(@GordThompson是足以证明结束与⅄确实有效,并且结束/导致相同的错误).

如果输入文件为空(即00)终止,则可以简单地是20 00的结束顺序,在这种情况下以换行符结尾可能会修复它.这也可以通过测试两行文件来证明:第1行是来自bad.txt的现有行,第2行是应该可以工作的行.例如:

291054  Ţawī Rifā‘
999999  test row,yo!

如果直接在上面显示的两行文件可以工作,那证明它是U 20xx代码点和该代码点的组合,它是发布错误的最后一个字符(比文件更多的传输).但是,如果这个两行文件也得到错误,那么它证明有一个U 20xx代码点作为字符串字段的最后一个字符是问题(并且假设即使在字符串字段不是行的最终字段,因为在这种情况下已经排除了传输的空终止符).

似乎这是一个freetds / freebcp的bug,或者也许有一个配置选项没有尝试修剪尾随空格,或者也许有一种方式让它看到这个字段为NCHAR而不是NVARCHAR.

UPDATE

@GordThompson和OP(@NeilMcGuigan)都已经测试并确认,无论文件中的字符串字段在何处,都会存在此问题:在行的中间,行的末尾,在最后一行,而不是在最后一行.因此这是一个普遍的问题.

实际上,我发现源代码,由于没有考虑多字节字符集,所以问题会发生.我将在GitHub存储库中提交一个问题. rtrim函数的来源在这里:

https://github.com/FreeTDS/freetds/blob/master/src/dblib/bcp.c#L2267

关于这个说法:

The sql Server uses UTF-16LE (though TDS starts with UCS-2LE and switches over I believe)

从编码的角度来说,UCS-2和UTF-16之间确实没有区别.字节序列是相同的.唯一的区别在于替代对的解释(即代码点高于U FFFF / 65535). UCS-2具有用于构建代孕对的代码点,但是当时没有任何替代对的实施. UTF-16简单地添加了代理对的实现,以创建补充字符.因此,sql Server存储和检索UTF-16 LE数据而没有问题.唯一的问题是内置函数不知道如何解释代理对,除非排序规则以_SC(用于补充字符)结束,并且这些排序在sql Server 2012中引入.

猜你在找的MsSQL相关文章