2016-05-13 54 views
0

背景:我正在SQL Server中编写存储过程以产生一些数据的导出。这些文件需要采用XML格式,所以我在查询中使用for xml进行了BCP处理。Unicode XML和Powershell转换为UTF-8的SQL Server BCP会产生奇怪的返回字符

字符串数据varchar(不nvarchar),但一些字符没有正确地转换,所以我们改变了BCP语句中使用-w,而不是-CACP。我们的最终用户不愿意使用文件大小,而是要求UTF-8,而我们希望放入XML指令语句,以便文件更“正确”的XML;一点Powershell似乎照顾两者。

一般来说,这个过程正在做我们想做的事情,但是里面含有CR/LF的数据存在一些奇怪现象 - 即,看起来BCP将CR转换为字符串“$#x0D;”,但是将LF保留为0x0A(当然,Unicode等同于0x00字节)。然后,Powershell语句似乎将“$#x0D;”作为更多的文本数据,它将(Unicode)0x0A变成(UTF-8)空间。这不是我们想要的!有趣的是,如果我离开XML指令部分,(Unicode)0x0A会被转换回(UTF-8)CR/LF,但会留下“$#x0D;”在数据中也是如此。

我提供了一个简化的例子,说明我们在下面做什么;将MyDatabase更改为正常工作的数据库,并将C:\路径更改为任何可行的工作,并且可以看到生成的文件 - 我使用TextPad以可视方式查看它们,并使用HexEdit检查实际的逐字节输出结果。有没有人看到任何明显的可能帮助?我一直在谷歌上搜索了一下,但似乎无法找到与我们的具体情况什么...

create table MyDatabase.dbo.TestTable (
    StringData varchar (1000) 
) 

insert into MyDatabase.dbo.TestTable (StringData) 
    select 
     'I have return characters in me.' + char (13) + char (10) + 'Will the file I''m output to be okay?' 

declare @Query varchar (2000) 
declare @Command varchar (2000) 

set @Query = 'select * ' 
      + 'from MyDatabase.dbo.TestTable with (nolock) ' 
      + 'for xml path (''StringData''), root (''TableData''), elements, type' 

set @Command = 'bcp "' + @Query + '" queryout C:\TestXMLUnicodeData_1.xml -w -T -S' + cast(@@ServerName as varchar) 

exec master.dbo.xp_cmdshell @Command 

set @Command = 'powershell "Get-Content C:\TestXMLUnicodeData_1.xml | Set-Content -Encoding UTF8 C:\TestXMLUTF8Data_1.xml' 

exec master.dbo.xp_cmdshell @Command 

set @Query = 'select * ' 
      + 'from MyDatabase.dbo.TestTable with (nolock) ' 
      + 'for xml path (''StringData''), root (''TableData''), elements, type' 

set @Command = 'bcp "' + @Query + '" queryout C:\TestXMLUnicodeData_2.xml -w -T -S' + cast(@@ServerName as varchar) 

exec master.dbo.xp_cmdshell @Command 

set @Command = 'powershell "''<?xml version=\"1.0\" encoding=\"UTF-8\"?>'' + (Get-Content C:\TestXMLUnicodeData_2.xml)' 
       + ' | Set-Content -Encoding UTF8 C:\TestXMLUTF8Data_2.xml' 

exec master.dbo.xp_cmdshell @Command 

回答

0

我不知道为什么FOR XML/BCP不编码LF还有CR。

但是,您在第二个文件中插入空间的原因是Get-Content和powershell的自动字符串数组枚举行为如何。

Get-Content默认每次读取一行,实际返回一个包含数据文件行的字符串数组。在您的示例中,Get-Content使用LF字符作为EOL终止符(EOL终止符随后会从数据中丢弃)。

查看此博客文章,了解Get-Content的工作原理。
http://powershell.org/wp/2013/10/21/why-get-content-aint-yer-friend/

因为PowerShell自动枚举字符串数组是如何被插入的。

$a = "One", "Two", "Three" 
$a 
Write-Output ("The string concatentation causes an automatic enumeration of the string array. Notice the automatic spaces inserted after data at index zero" + $a) 

使用上为博客推荐的Get-Content命令的-raw选项似乎得到你要找的,因为获取内容的输出会返回一个数组的一个项目,以便有附加没有自动空间。

+0

谢谢尼克。不幸的是,我认为这是造成最大麻烦的第一件事。我最终在流程开始时进行了更多的工作,以明确编码CR和LF,并使用Powershell将编码后的字符串替换为结尾处的CR/LF。毕竟,还有几个CPU周期? : - / –