命令行查找和替换后,特殊字符变成了问号

4

我有一个文本文件input.xlf

  <trans-unit id="loco:5e7257a0c38e0f5b456bae94">
    &lt;source&gt;Login</source>
    <target>登入</target>
    <note>Login Header</note>
  </trans-unit>

基本上我需要用 < 代替 &lt;,用 > 代替 &gt;,因此我运行以下脚本:

runner.bat

powershell -Command "(gc input.xlf) -replace '&lt;', '<' | Out-File -encoding ASCII output.xlf";
powershell -Command "(gc output.xlf) -replace '&gt;', '>' | Out-File -encoding ASCII  output.xlf";

上述代码在我发现以下输出后便不再起作用。
  <trans-unit id="loco:5e7257a0c38e0f5b456bae94">
    <source>Login</source>
    <target>??????</target>
    <note>Login Header</note>
  </trans-unit>

我尝试移除编码,但现在我得到了:

 <trans-unit id="loco:5e7257a0c38e0f5b456bae94">
   <source>Login</source>
   <target>登入</target>
   <note>Login Header</note>  
 </trans-unit>

以下是我期望的输出结果

  <trans-unit id="loco:5e7257a0c38e0f5b456bae94">
    <source>Login</source>
    <target>登入</target>
    <note>Login Header</note>
  </trans-unit>

1
你正在使用 ASCII 编码,它不支持 Unicode 字符。 - Hazel へいぜる
2
这是因为你使用了“-encoding ASCII”,你尝试过“-encoding UTF8”吗? - tromgy
@tromgy 刚试了一下,我得到的结果是 登录,与没有编码时的结果相同。 - Owen Kelvin
3
看看这篇文章是否有帮助:https://dev59.com/r1UM5IYBdhLWcg3wAsZF。不确定,而且我感觉不太舒服,所以不想过多关注它,但它可能会有所帮助。 - Hazel へいぜる
感谢 @Tacoタコス,这篇帖子提供了很多有用的信息。 - Owen Kelvin
1个回答

7

涉及到(可能)两个字符编码问题:

  • 输出时,使用 -Encoding Ascii有损地将任何非ASCII字符转换为字面上的 ? 字符。

    • 要保留所有字符,您必须选择一个Unicode编码,如 -Encoding Utf8
  • 输入时,必须确保PowerShell正确读取输入文件。

    • 具体而言,Windows PowerShell错误地将没有BOM的UTF-8文件解释为ANSI编码,因此您还需要对Get-Content使用-Encoding Utf8

另外,您可以使用一个powershell.exe调用,并且还可以优化此调用:

powershell -Command "(gc -Raw -Encoding utf8 input.xlf) -replace '&lt;', '<' -replace '&gt;', '>' | Set-Content -NoNewLine -Encoding Utf8 output.xlf"
  • 在使用gc (Get-Content)读取文件时,使用-Raw会将整个文件作为一个整体读入,而不是将其读入成为一个行数组,这将加速-replace操作。

  • 你可以链式使用-replace操作。

  • 对于已经是文本(字符串)的输入,通常选择Set-Content会更快。[1]
    -NoNewLine 可以防止额外的换行符被添加到末尾。


[1] 在此例中,由于仅写入了单个字符串,所以差异几乎可以忽略不计,但使用许多输入字符串(按行输出),则可能会有所不同 - 请参见此答案。


1
这非常有帮助,因为这些是翻译文件,而且它们相当大。 - Owen Kelvin
1
很高兴听到这个好消息,@OwenKelvin。是的,-Raw 有很大的区别。唯一的注意事项是文件内容必须整体适合内存(实际上在这里是三倍,因为每个 -replace 操作都会创建一个副本),但即使是大型文本文件也很可能适合。 (由于将 Get-Content 调用括在 (...) 中,您自己的方法没有使用 -Raw 也会加载整个文件,尽管可以将其转换为流式处理方法,其中每行都会被逐个处理并保存到目标文件中,而无需一次性将整个文件的内容存储在内存中。) - mklement0

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