我们都知道 UTF-8 很难。我从 Facebook 导出消息,生成的 JSON 文件将所有非 ASCII 字符转义为 unicode 代码点。
我正在寻找一种简单的方法来将这些 unicode 代码点转义为常规的旧 UTF-8。我也很想使用 PowerShell。
我试过了
$str = "\u00f0\u009f\u0091\u008d"
[Regex]::Replace($str, "\\[Uu]([0-9A-Fa-f]{4})", `
{[char]::ToString([Convert]::ToInt32($args[0].Groups[1].Value, 16))} )
但这只会给我 ð 结果,而不是 👍。
我也尝试使用 Notepad++,我发现了这篇文章:How to conversion escaped Unicode (e.g.
\u0432\u0441\u0435
) to UTF-8 chars (все) in Notepad++。接受的答案也与上面的示例完全相同:ð。
我在这里找到了解码解决方案:UTF8.js库可以完美解码文本,您可以在这里尝试一下(使用
\u00f0\u009f\u0091\u008d
作为输入)。
PowerShell中有没有办法解码
\u00f0\u009f\u0091\u008d
来接收👍?我希望在导出的 Facebook 消息中包含真正的 UTF-8,这样我就可以真正阅读它们。
帮助我理解
\u00f0\u009f\u0091\u008d
实际代表什么(除了它是 一些 UTF-8 十六进制表示)的奖励积分。为什么它与 C++ 中的 U+1F44D
或 \uD83D\uDC4D
相同?
U+1F44D
。
使用可变长度 UTF-8 编码,需要以下 4 字节(以十六进制数字表示)来表示此代码点:
F0 9F 91 8D
.
虽然这些字节在您的字符串中是可识别的,
$str = "\u00f0\u009f\u0091\u008d"
它们不应该被表示为
\u
转义码,因为它们不是Unicode代码单元/代码点,它们是字节。
使用 4 十六进制数字转义序列 (UTF-16),正确的表示需要 2 16 位 Unicode 代码 units,即所谓的代理对,它们一起表示单个非 BMP 代码 点
U+1F44D
:
$str = "\uD83D\uDC4D"
如果您的 JSON 输入使用了正确的 Unicode 转义,PowerShell 将正确处理该字符串;例如:
'{ "str": "\uD83D\uDC4D" }' | ConvertFrom-Json > out.txt
如果您检查文件
out.txt
,您会看到类似以下内容:
str
---
👍
(输出被发送到文件,因为控制台窗口无法正确渲染 👍char.,至少在没有额外配置的情况下是这样;但是请注意,如果您在 Linux 或 macOS 上使用 PowerShell Core,终端输出将起作用.)
因此,最好的解决方案是从源头上纠正问题并使用正确的 Unicode 转义(或者甚至使用字符本身,只要源支持任何标准 Unicode 编码)。
如果您确实必须解析损坏的表示,请尝试以下解决方法(PSv4+),建立在您自己的
[regex]::Replace()
技术之上:
$str = "A \u00f0\u009f\u0091\u008d for Mot\u00c3\u00b6rhead."
[regex]::replace($str, '(?:\\u[0-9a-f]{4})+', { param($m)
$utf8Bytes = (-split ($m.Value -replace '\\u([0-9a-f]{4})', '0x$1 ')).ForEach([byte])
[text.encoding]::utf8.GetString($utf8Bytes)
})
这应该会产生
A 👍 for Motörhead.
上面将
\u...
转义序列转换为它们表示的字节值,并将生成的字节数组解释为 UTF-8 文本。
要将解码后的字符串保存到 UTF-8 文件,请使用 ... | Set-Content -Encoding utf8 out.txt
Out-File
,因此它是虚拟别名,
>
,default 通过 PowerShell 的全局参数默认值哈希表转换为 UTF-8:
$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'
但请注意,在 Windows PowerShell(而不是 PowerShell
Core)上,在这两种情况下您都会获得一个带有 BOM 的 UTF-8 文件 - 避免这种情况需要直接使用 .NET 框架:请参阅使用PowerShell 以 UTF-8 格式写入不带 BOM 的文件
$text=[regex]::Unescape("A \u00f0\u009f\u0091\u008d for Mot\u00c3\u00b6rhead.")
Write-Host "[regex]::Unescape(utf-8) = $text"
$encTo=[System.Text.Encoding]::GetEncoding('iso-8859-1') # Change it to yours (iso-8859-2) i suppose
$bytes = $encTo.GetBytes($Text)
$text=[System.Text.Encoding]::UTF8.GetString($bytes)
Write-Host "utf8_DecodedFrom_8859_1 = $text"
[正则表达式]::Unescape(utf-8) = A ð 代表 Motörhead。
utf8_DecodedFrom_8859_1 = A 👍 代表 Motörhead。
不好的是——队伍会很长。 (前 2 个半字节“00”是浪费)
我必须承认,mklement0 的例子很迷人。
编码代码——只有一行!!!:
$emoji='A 👍 for Motörhead.'
[Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null
$str=(([System.Web.HttpUtility]::UrlEncode($emoji)) -replace '%','\u00') -replace '\+',' '
$str
您可以通过标准 url 方式对其进行解码:
$str="A \u00f0\u009f\u0091\u008d for Mot\u00c3\u00b6rhead."
$str=$str -replace '\\u00','%'
[Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null
[System.Web.HttpUtility]::UrlDecode($str)
Motörhead 的 👍。