如何更改我的Powershell脚本,以使其使用ANSI - Windows-1252编码进行文件输出?

11
我有一个银行应用程序脚本,可以通过从每日输入的银行文件中删除错误记录来生成“过滤”输出文件(请参见如何创建一个Windows Server脚本以从结果写入到新文件的文件中删除错误记录和每个先前的记录)。将向州发送“过滤”输出文件以更新其系统。顺便说一句,我们从银行接收到的原始输入文件在我的文件编辑器(UltraEdit)中显示为Unix 1252(ANSI Latin 1),每个记录仅以换行符结束。
我向州发送了从“干净”(无错误)和“肮脏”(包含4个错误)的输入文件生成的几个测试输出文件进行测试,以确保在实施之前一切正常,但是有点担心,因为输出文件是以UTF-16编码并带有CRLF行结尾,而输入和当前未过滤的输出都以Windows-1252编码。此系统上的所有其他输出文件都是Windows-1252编码。
果然...我得到了回复,说编码对于国家的系统不正确。他们的评论是: “该文件已编码为UCS-2 Little Endian,需要转换为ANSI才能在我们的系统上运行。那是出乎意料的。”
“没有详细交易的文件会通过我们的EFT拒绝程序运行。”
“它似乎已经被处理了,但我们必须做一些转换。可以发送ANSI格式还是需要以UCS 2 Little Endian格式完成?”
我尝试不成功地将-编码“Windows-1252”和-编码windows-1252添加到我的out-file语句中,两者都返回以下消息: Out-File:无法验证参数'Encoding'。 参数“Windows-1252”不属于由ValidateSet属性指定的集合“unknown,string,unicode,bigendianunicode,utf8,utf7,utf32,ascii,默认值,oem”。 提供一个在集合中的参数,然后再尝试命令。 在C:\ EZTRIEVE \ PwrShell \ TEST2_FilterR02.ps1:47 char:57 + ... OutputStrings | Out-File $OutputFileFiltered -Encoding "Windows-1252" + ~~~~~~~~~~~~~~ + CategoryInfo:InvalidData:(:) [Out-File],ParameterBindingVal idationException + FullyQualifiedErrorId:ParameterArgumentValidationError,Microsoft.Power Shell.Commands.OutFileCommand
我已经仔细寻找了几天关于这个问题的帮助,但是没有什么内容真正清晰明了,而且我发现大部分都是将编码从Windows-1252转换为其他编码。昨天我在stackoverflow的某个评论中发现“ANSI”与Windows-1252相同,但到目前为止,我还没有找到任何东西来展示如何正确地将Windows-1252编码选项附加到我的out-file语句中以便Powershell接受它。我真的需要完成这个项目,这样我才能处理我的队列中添加的下几个项目。是否可能有一个子参数我错过了,需要附加到-Encoding?
此内容在运行Windows Server 2016标准版和Powershell 5.1的新备份服务器上,在Dollar Universe(任务调度程序)下进行测试。我们的生产系统在运行Windows Server 2012 R2上的Dollar Universe,也使用Powershell 5.1。(是的,我们正在寻找足够的升级时间窗口 :-))
截至最后一次尝试,我的Powershell脚本是:
 [cmdletbinding()]
 Param
 (
     [string] $InputFilePath
 )   

 # Read the text file
 $InputFile = Get-Content $InputFilePath

# Initialize output record counter
$Inrecs = 0
$Outrecs = 0

# Get the time
$Time = Get-Date -Format "MM_dd_yy"

# Set up the output file name
$OutputFileFiltered = "C:\EZTRIEVE\CFIS\DATA\TEST_CFI_EFT_RETURN_FILTERED"

# Initialize the variable used to hold the output
$OutputStrings = @()

# Loop through each line in the file
# Check the line ahead for "R02" and add it to the output
# or skip it appropriately
for ($i = 0; $i -lt $InputFile.Length - 1; $i++)
{
    if ($InputFile[$i + 1] -notmatch "R02")
    {
        # The next record does not contain "R02", increment count and add it to the output
        $Outrecs++
        $OutputStrings += $InputFile[$i]
    }
    else
    {
        # The next record does contain "R02", skip it
        $i++
    }
}

# Add the trailer record to the output
$OutputString += $InputFile[$InputFile.Length - 1]

# Write the output to a file
# $OutputStrings | Out-File $OutputFileFiltered
$OutputStrings | Out-File $OutputFileFiltered -Encoding windows-1252

# Display record processing stats:

$Filtered = $Outrecs-$i

Write-Host $i  Input records processed

Write-Host $Filtered  Error records filtered out

Write-Host $Outrecs  Output records written

我不认为有类似Unix CP252和Windows CP252这样的东西,相反,格式是相同的,只是一个CP252,在第一个中,行是通过仅使用换行符而不是通常的回车+换行符分隔的。 因此,“转换”需要用\n替换特殊字符\r\n。 如果应用$OutputStrings.ToString().Replace("n","r`n") | Out-File $OutputFileFiltered会发生什么情况? 如果您包括一个可运行的PowerShell脚本示例,那么最终获取帮助会更容易。 - A. Lion
@P.Lion:你说得对,只有一个Windows-1252代码页。然而,在这里Unix风格的LF-only换行符不是问题(在PowerShell中很少出现这种情况,因为它同样识别LF和CRLF换行符):当Get-Content返回输入文件的行作为数组时,换行符(无论是LF-only还是CRLF)都会被_剥离_。在稍后使用Out-File(或Set-Content等)输出时,单个字符串将与_适用于平台的_换行序列连接,这意味着在Windows上你最终会得到CRLF换行符的文件。 - mklement0
@AdminOfThings:-使用ASCII编码会使输出以CRLF行结尾,我只需要LF行结尾。 - K9-Guy
@K9-Guy:是的,在Windows上,Out-File(和Set-Content)会给你CRLF换行符。PowerShell Core表现出相同的行为,因为换行符的行为与操作系统相关,而不是与PowerShell版本相关。我的答案底部部分向您展示了如何无条件地创建LF文件。您尝试过这样做了吗?有什么问题吗? - mklement0
@K9-Guy 如果您使用notepad++打开Linux-CP252文件,您可以轻松地看到它。一旦打开,请使用CTRL + F,然后选择替换选项卡,并将其填写如下: 搜索:\n 替换为:\r\n 并将研究类型设置为“扩展(\n,\r..)” 这将将每个行中断从仅为LineFeed更改为CRLF,这基本上是类似于Linux的文本文件和类似于Windows的文本文件之间格式的区别,假设它们都使用相同的编码类型。 - A. Lion
显示剩余6条评论
1个回答

14

注意:

  • 您后来澄清了您需要LF(Unix风格)换行符-请参见底部部分。

  • 接下来的部分涉及最初提出的问题,并提供导致具有CRLF(Windows风格)换行符的文件的解决方案(在Windows上运行时)。


如果您的系统的非Unicode程序语言设置(又名系统语言环境)恰好将Windows-1252作为活动的ANSI代码页(例如,在美国英语或西欧系统上),使用-Encoding Default,因为Default在Windows PowerShell中指该代码页(但在PowerShell Core中不是这样,默认情况下为无BOM的UTF-8并且不支持Default编码标识符)。

使用以下命令进行验证:(Get-ItemPropertyValue HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage ACP) -eq '1252'

... | Out-File -Encoding Default $file
注:
- 如果您确定您的数据实际上仅由ASCII范围的字符组成(代码点在7位范围内的字符,不包括带有重音符号的字符,如 `ü`),则即使您的系统语言环境使用的是Windows-1252之外的ANSI代码页,`-Encoding Default`也可以工作,因为所有(单字节)ANSI代码页共享其7位子范围内的所有ASCII字符; 您还可以使用`-Encoding ASCII`,但请注意,如果最终存在非ASCII字符,则它们将被转换为文本?字符,导致信息丢失。
- 在Windows PowerShell(但不是PowerShell Core,那里的一致默认值为UTF-8 without BOM)中,`Set-Content` cmdlet实际上默认使用`Default`编码。
- 虽然`Set-Content`的字符串化行为与`Out-File`不同 - 参见这个答案- 但如果要写入文件的对象已经是字符串,则`Set-Content`实际上是更好的选择。
否则,您有两个选择:
- 直接使用.NET Framework文件I / O功能,其中可以使用.NET支持的任何编码;例如:
  $lines = ...  # array of strings (to become lines in a file)
  # CAVEAT: Be sure to specify an *absolute file path* in $file,
  #         because .NET typically has a different working dir.
  [IO.File]::WriteAllLines($file, $lines, [Text.Encoding]::GetEncoding(1252))
  • 使用 PowerShell Core,允许你将任何支持的.NET编码传递给
    -Encoding 参数:

  •   ... | Out-File -Encoding ([Text.Encoding]::GetEncoding(1252)) $file
    

    请注意,在 PSv5.1+ 中,您可以实际上更改由 >>> 运算符使用的编码,详见此答案
    然而,在 Windows PowerShell 中,您仍然受到 Out-File-Encoding 参数支持的编码的限制。


    在 Windows 上使用 LF(Unix 样式)换行符创建文本文件:

    PowerShell(始终如一)和 .NET(默认情况下)在将字符串作为行写入文件时使用适合平台的换行序列,如[Environment]::NewLine所反映的那样。换句话说:在 Windows 上,您最终会得到带有 CRLF 换行符的文件,并且在类 Unix 平台上(PowerShell Core)则是 LF 换行符。

    请注意,以下解决方案假设要写入文件的数据是一个字符串数组,表示要写入的行,例如由Get-Content返回的(其中结果数组元素是输入文件的行,不包括其尾随的换行符序列)。

    要在 Windows 上显式地创建一个具有 LF 换行符的文件(PSv5+):

    $lines = ...  # array of strings (to become lines in a file)
    
    ($lines -join "`n") + "`n" | Set-Content -NoNewline $file
    

    "`n" 会产生一个换行符。

    注意:

    • 在Windows PowerShell中,它会隐式使用活动ANSI代码页的编码。

    • 在PowerShell Core中,它会隐式创建一个没有BOM的UTF-8文件。如果您想使用活动ANSI代码页,请使用:

    • -Encoding ([Text.Encoding]::GetEncoding([int] (Get-ItemPropertyValue HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage ACP)))
      

    PSv4-(PowerShell 4 版本或更低版本)中,您必须直接使用 .NET Framework:

    $lines = ...  # array of strings (to become lines in a file)
    
    
    # CAVEAT: Be sure to specify an *absolute file path* in $file,
    #         because .NET typically has a different working dir.
    [IO.File]::WriteAllText($file, ($lines -join "`n") + "`n")
    

    注意:

    • 在Windows PowerShell和PowerShell Core中,这将创建一个没有BOM的UTF-8文件。

    • 如果想要使用活动的ANSI代码页,请将以下内容作为附加参数传递给WriteAllText()

    ([Text.Encoding]::GetEncoding([int] (Get-ItemPropertyValue HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage ACP)))
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接