2016-05-13 57 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週期? : - / –