使用Powershell删除Unicode字符

3

我在Excel中使用vlookup时遇到了一些问题。 我已经看到了这个问题,但我还没有找到解决方案。

我有一个包含Unicode字符的大量文本文件行。

例如: 这一行:'S0841488.JPG06082014‏‎08.21' 包含这两个Unicode字符:U+200F U+200E 'S0841488.JPG06082014 U+200F U+200E 08.21。

请告诉我如何使用Powershell去除这些Unicode字符。


这是一个常见的问题。请参阅https://dev59.com/1broa4cB1Zd3GeqPu_k-。 - js2010
2个回答

13
如果你想要移除所有落在ASCII范围之外的字符(Unicode代码点范围U+0000 - U+007F):
# Removes any non-ASCII characters from the LHS string,
# which includes the problematic hidden control characters.
'S0841488.JPG06082014‏‎08.21' -creplace '\P{IsBasicLatin}'

解决方案使用了-creplace,这是基于正则表达式的-replace操作符的区分大小写的变体[1],使用了Unicode块名称IsBasicLatin的否定形式(\P)。它指的是Unicode的ASCII子范围。简而言之:\P{IsBasicLatin}匹配任何非ASCII字符,并且由于没有指定替换字符串,它有效地删除它;结合-creplace在输入字符串中无论如何都会替换所有匹配项,所有非ASCII字符都被删除。
注意:
如果您希望删除位于ISO-8859-1范围之外的字符(Unicode代码点范围U+0000 - U+00FF),其中包括重音字符如é,请使用以下方法:
# 删除非ISO-8859-1字符。 # -> 'Café £',即'€'和'—'(破折号)被删除, # 但'é'和'£'被保留。 'Café €—£' -creplace '[^\p{IsBasicLatin}\p{IsLatin-1Supplement}]'
注意:ISO-8859-1与Windows-1252在很大程度上是相同的,但并非完全相同,其中一个显著的后果是缺少'€',如上所示。您可以在上述字符集表达式([...])中手动包含缺失的字符,以实现完全的Windows-1252兼容性:€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ
考虑到与Unicode代码点范围的相关性,您还可以使用更简洁但不太描述性的解决方案:
- 使用'... -replace '[^\x00-\x7F]' 保留仅ASCII范围字符。 - 使用'... -replace '[^\x00-\xFF]' 保留仅ISO-8859-1范围字符。
你可以通过使用Debug-String函数来验证,它可以有效地从字符串中移除(不可见的)左到右标记U+200E和右到左标记U+200F字符。该函数是作为一个MIT许可的Gist提供的。
# Download and define the Debug-String function.
# NOTE: 
#  I can personally assure you that doing this is safe, but you
#  you should always check the source code first.
irm https://gist.github.com/mklement0/7f2f1e13ac9c2afaf0a0906d08b392d1/raw/Debug-String.ps1 | iex


# Visualize the existing non-ASCII-range characters
'S0841488.JPG06082014‏‎08.21' | Debug-String -UnicodeEscapes

# Remove them and verify that they're gone.
'S0841488.JPG06082014‏‎08.21' -replace '\P{IsBasicLatin}' | Debug-String -UnicodeEscapes

以上产生以下结果:
S0841488.JPG06082014`u{200f}`u{200e}08.21
S0841488.JPG0608201408.21

请注意在原始输入字符串中,不可见控制字符的可视化为`u{200f}`u{200e},并且在应用-replace操作后,它们不再存在。
在PowerShell(Core)7+(但不适用于Windows PowerShell)中,这种Unicode转义序列也可以在可扩展字符串中使用,即在双引号字符串字面值内(例如,"Hi`u{21}"扩展为直接输出Hi!)- 请参阅概念性about_Special_Characters帮助主题。
[1] 请参考this answer,了解为什么必须使用区分大小写的匹配方式。
尽管操作符是区分大小写的,但本质上不区分大小写的\P{L}正则表达式块名称构造仍然排除小写字母(而\P{Lu} / \P{Ll}只会排除大写字母/小写字母)。

"Véronique & “Cheris" -creplace '\P{IsBasicLatin}' 也会移除 é 这个有效的拉丁字符。有没有办法保留这个字符? - undefined
1
@DarkLite1,请使用'"Véronique & “Cheris"' -creplace '[^\p{IsBasicLatin}\p{IsLatin-1Supplement}]' - undefined
1
@DarkLite1,请查看我的更新,并注意关于ISO-8859-1和Windows-1252之间的陷阱。 - undefined

0
如果它们被写成你所说的 'U+200F' 和 'U+200E',那么基本的替换就可以完成。只需要在 + 前面加上转义字符,因为它在正则表达式中有特殊含义。
正则表达式解释:
\s - 表示空格。
? - 可能存在,也可能不存在。
\ - 在 + 前面是一个转义字符。
[EF] - 表示字母 E 或字母 F。
('|.$) - 单引号或点号在末尾。
# Path to your file and new file where no unicode characters.
$Path = "C:\JustForExample\FileThatContainsThisLines.txt"
$NewPath = "C:\JustForExample\FileThatContainsThisLines2.txt"

# Getting content of file.
$content = Get-Content -Path $Path

# Removing unicode characters.
$newContent = $content -replace "\s?U\+200[EF]\s?"

# For removing quot and dot at the end.
$newContent = $newContent -replace "('|\.$)" 

# Saving content to new Path.
# If you need to replace them inside the file, so just change NewPath to Path.
$newContent | Set-Content -Path $NewPath

所以,替换简单应该删除与该模式匹配的内容。


嗨Puzo谢谢你,但是很抱歉,它没有按预期工作。我使用这个URL来查看Unicode字符: https://www.soscisurvey.de/tools/view-chars.php
  • 在运行脚本之后,字符并没有被移除。 结果是: S0841488.JPG06082014‏‎ U+200F U+200E 08.21.
- Larry
嗯... 我的主要问题是我无法重现它以检查为什么它不起作用。也许你可以尝试将所有内容转换为UTF8,然后它们就会变得可见。$MyRawString = Get-Content -Raw $Path $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False [System.IO.File]::WriteAllLines($NewPath, $MyRawString, $Utf8NoBomEncoding)你能分享一些示例文件吗? - Puzo
希望的是从字符串中删除实际的Unicode字符 - 类似U+200F这样的东西只是对那些字符的“可视化”,它们本身是“不可见”的。如果你复制并粘贴问题中“这一行:”后面的字符串,你将得到一个包含这些隐藏字符的字符串。 - mklement0
1
使用以下命令删除所有超出ASCII范围的字符: $newContent = $content -replace '\P{IsBasicLatin}'然而,这样做会产生一个新的错误:所有的大写字母'I'都被移除了。 - Larry
在一个元注释上,@Larry:除非你@提到我(就像我刚才用你的用户名做的那样),否则我不会收到后续评论的通知。如果你评论“我的”答案(这将更好,并且任何后续评论都应该在那里发布),你就不需要使用@提到。 - mklement0
@Larry,关于i / I问题:你触发了一个严重的错误,在 Windows PowerShell 中已经得到修正,在 PowerShell (Core) 中请参考我的更新答案。 - mklement0

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