Powershell - ASCII编码将特殊字符更改为问号

时间:2017-10-27 17:14:12

标签: powershell encoding utf-8 ascii

我正在使用Powershell脚本将字符串转换为XML然后导出到文件(以这种方式保持缩进):

[xml]$xmloutput = $xml
$sw = New-Object System.IO.StringWriter
$writer = New-Object System.Xml.XmlTextWriter($sw)
$writer.Formatting = [System.Xml.Formatting]::Indented
$xmloutput.WriteContentTo($writer)
$sw.ToString() | Set-Content -Encoding 'ASCII' $filepath

由于供应商限制,目标必须是ASCII格式。我看到的问题是ASCII只是将特殊字符更改为问号(例如:Ö变成?)。

如果我使用UTF8编码,输出看起来完全正常。我甚至尝试保存到UTF8然后转换为ASCII,做同样的事情(导出一个问号):

[System.Io.File]::ReadAllText($filepath) | Out-File -FilePath $filepath -Encoding ASCII

如果我在转换为XML之前尝试替换字符串中的字符(使用ASCII代码Ö),它只会转换&符号并保留其余字符,使其无效。

有没有办法让Powershell正确地将这些字符保存到文件中?

编辑:我想在输出文件中看到特殊字符,但如果不符合ASCII格式,我希望看到它的ASCII代码(在本例中为&#214;)< / p>

我也不想只看到O,我需要真正的角色。

2 个答案:

答案 0 :(得分:4)

XML文档中的所有字符都是Unicode。但是,XML文档的表示具有文档编码。不是该字符集成员的字符被写为字符实体引用,通常以数字形式和十六进制表示法。该数字是Unicode代码点。

您的合作伙伴的要求似乎是使用ASCII作为文档编码。

XmlDocument有点难以使用,但带有文档编码设置的XmlWriter可以使用:

$myString = 'hellÖ'

[xml]$myXml = [System.Management.Automation.PSSerializer]::Serialize($myString)

$settings = New-Object System.Xml.XmlWriterSettings
$settings.Encoding = [System.Text.Encoding]::ASCII
$settings.Indent = $true

$writer = [System.Xml.XmlWriter]::Create("./test.xml", $settings)
$myXml.Save($writer)
$writer.Dispose()

这会发出一个ASCII编码的文本文件,其中包含XML declation,声明文档编码为ASCII,并使用十六进制数字字符实体引用,用于无法用ASCII表示的XML内容字符:

<?xml version="1.0" encoding="us-ascii"?>
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <S>hell&#xD6;</S>
</Objs>

正如您在C1 Controls和Latin-1 Supplement块中看到的here,U + 00D6(&amp;#D6;),是带有DIAERESIS的L拉丁文大写字母O

答案 1 :(得分:2)

这不是特定于PowerShell的,它通常是一个字符编码问题。

基本上,该字符不是ASCII,而是ISO 8859-1。

但是,通过将XmlTextWriter直接写入文件可以简化此过程,因为您可以使用它来控制编码。试试这个:

$myString = 'hellÖ'

[xml]$myXml = [System.Management.Automation.PSSerializer]::Serialize($myString)

$myEncoding = [System.Text.Encoding]::GetEncoding('iso-8859-1')

$writer = New-Object System.Xml.XmlTextWriter($filepath, $myEncoding)
$writer.Formatting = [System.Xml.Formatting]::Indented

$myXml.WriteContentTo($writer)

$writer.Flush()
$writer.Close()
$writer.Dispose()

这将使用ISO 8859-1编码编写文件,但不会编码为XML实体。

因此,如果您的应用程序仅需要真正的ASCII,没有扩展集,那么这将无效。如果它真的只需要单字节编码并且这种编码中的字符集足够,那就没关系。

如何使用实体:

第1步:忽略我写的内容,改为使用Tom Blodget's answer

您可以做的是在ASCII编码器上设置custom fallback callback,这样每当遇到无法用ASCII表示的字符时,它就会调用您的函数来获取替换。您的函数将有助于返回角色的实体版本。

技术上......这可能会适得其反。由于您必须从编码器返回&符号&,因此XmlWriter可能会看到并“帮助”将其替换为&amp;,这会破坏您的编码。

可能直接从PowerShell使用此回调,但会有点麻烦。使用一些C#和Add-Type会更容易。

或者您可以执行此方法的游击版本:编写XML字符串,然后手动替换任何非ASCII字符。

这里我使用的是正则表达式引擎的替换方法版本,该方法采用匹配评估功能。正则表达式匹配任何不在'BasicLatin'Unicode Named Block中的字符。

$myString = 'hellÖ'

[xml]$myXml = [System.Management.Automation.PSSerializer]::Serialize($myString)

$sw = New-Object System.IO.StringWriter
$writer = New-Object System.Xml.XmlTextWriter($sw)
$writer.Formatting = [System.Xml.Formatting]::Indented
$myXml.WriteContentTo($writer)

$output = [RegEx]::Replace($sw.ToString(), '\P{IsBasicLatin}', { param($match) '&#{0};' -f [int][char]$match.Value })
$output  | Set-Content -Encoding 'ASCII' -LiteralPath $filepath

据我所知,这将完全符合您的要求。