【问题标题】:Script to convert unicode characters in <U9999> format to their ASCII equivalents将 <U9999> 格式的 unicode 字符转换为其 ASCII 等价物的脚本
【发布时间】:2015-02-12 05:44:52
【问题描述】:

我正在对 Linux 语言环境文件 /usr/share/i18n/locales(如 pt_BR)进行一些更改,以更改日期、时间、数字等的默认格式。但由于 unicode 字符以 &lt;U9999&gt; 格式的字符串呈现,因此文本是很难阅读。

这是它的一个sn-p:

LC_TIME
abday   "<U0044><U006F><U006D>";"<U0053><U0065><U0067>";/
    "<U0054><U0065><U0072>";"<U0051><U0075><U0061>";/
    "<U0051><U0075><U0069>";"<U0053><U0065><U0078>";/
    "<U0053><U00E1><U0062>"

那么,如何制作一个简单的脚本(可能是 bash、python、pearl 等等)来将这个替换 &lt;Uxxxx&gt; 代码的文本转换为它们的 ASCII 等价物? (是的,它们都是低于 255 的 ASCI 字符,大多数甚至低于 127)

如果收到多个答案,我会接受最优雅和/或更详细解释的答案(如命令中使用的选项和标志)

例如,上面的文本将被转换为:

LC_TIME
abday   "Dom";"Seg";/
    "Ter";"Qua";/
    "Qui";"Sex";/
    "Sáb"

另一个可以做相反的脚本的奖励点:将给定字符串的所有字符转换为&lt;Uxxx&gt; 格式。

谢谢!

【问题讨论】:

  • 我认为 XML 万神殿中的工具可以更好地处理这个问题。我建议你用 XML 标记这篇文章(也许 XSLT 也是?)。祝你好运!
  • 之所以使用 Unicode 表示法,是因为并非所有 Unicode 字符都有对应的 ASCII 字符。那么,你想对没有 ASCII 等价物的 Unicode 序列做什么(这是大多数 Unicode 序列 - 按可能序列的数量;不一定是大多数使用序列的数量)。
  • 字符 U0080 到 U00FF(即从 128 到 255 的十进制)是 NOT ASCII。它们必须使用您选择的编码进行转换,在您的情况下可能是 latin1
  • @shelter:好的,我添加了标签。简单的 linux 脚本(bash、python 等)中是否有任何处理 XML 的工具?
  • @Jonathan/John:是的,但在该文件 (pt_BR) 中,使用的 unicode 的 99% 实际上是 U0020 和 U0079 之间的 asci 字符。 80到FF之间的很少,没有超过FF的。所以它们可以在我的系统中完美打印。我不介意几个字符出错,只要 99% 的文本变得可读。它非常检查 ASCI 表非常耗时,逐个字符地解码字符串,如“%d %Y %z %HH:%MM”或“%d-%m-% Y”或“星期六”。我想更改日期、货币格式,但需要数小时才能解码,然后重新编码。因此需要一个脚本来帮助

标签: python xml bash unicode


【解决方案1】:

使用字段

#!/bin/bash

awk -F'<U0+|>' '{
    for(i=1;i<=NF;i++)
        if($i ~ "^[0-9A-F]+$")
            $i=sprintf("%c", strtonum("0x"$i))
}1' OFS="" /path/to/infile

说明

  1. -F'&lt;U0+|&gt;':这就是让这个脚本如此简短的魔力。我们告诉 awk 字段分隔符是 &lt;U0+ 或简单的 &gt;。这样做的好处是 awk 会为我们自动剥离这些字符,因此在进行 strtonum() 转换时我们不必手动使用 gsub() 进行。

  2. for(i=1;i&lt;=NF;i++):遍历每个字段

  3. if($i ~ "^[0-9A-F]+$"):检查当前字段是否仅由十六进制数字组成。请记住,由于上面的#1,&lt;U006F&gt; 这样的东西此时将被视为6F
  4. $i=sprintf("%c", strtonum("0x"$i)):将十六进制数字替换为其对应的 ascii 值。我们必须在字段 $i 前面加上 "0x" 以便 awk 知道它的十六进制值
  5. }1:强制print 的快捷方式或总是打印每一行
  6. OFS="":将输出字段分隔符设置为空字符串。如果我们不这样做,我们将在输出中出现&lt;U0+&gt; 的任何地方都有空格

使用 match() [需要 gawk]

#!/bin/bash

gawk '{
    while(match($0, /<U[0-9A-F]+>/)){
        pat = substr($0,RSTART,RLENGTH)
        gsub(/U0+|[<>]/,"",pat)
        asc = sprintf("%c", strtonum("0x"pat))
        $0 = substr($0, 1, RSTART-1) asc substr($0, RSTART+RLENGTH)
    }
}1' /path/to/infile

【讨论】:

  • 目前看起来很棒!生病测试它,看看它是否有效。愿意解释使用的参数吗?我以前从未玩过 awk 命令。另外,我猜反向脚本会更简单(从“abcd”到“”),因为它不需要任何正则表达式匹配。
  • @MestreLion:看看我的新优化版本。我会记下它是如何工作的
  • 成功了!我将 /path/to/infile 更改为 "$1" 并将脚本保存为 "unicode-decode",并使用 "en_US" 和 "pt_BR" 进行了测试,它对第一个非常有效,对另一个很好。像“Sábado”和“Março”这样的词被破坏了,但这是意料之中的,没什么大不了的(我只希望 gedit 对此不那么挑剔,它拒绝打开文件),但我可以找到结果并继续阅读屏幕,所以很好。
  • +1 用于优化。现在 80 到 FF 之间的字符不会破坏屏幕结果(终端将它们显示为单个“?”黑白字符),并且 gedit 接受文本并完美显示正确的字符!!!太棒了!!!
  • @MestreLion:很高兴它对你有用,对于
【解决方案2】:

这是一个 Python 脚本,它使用 unidecode module&lt;U9999&gt; 字符串转换为其 ASCII (0-127) 等效项:

#!/usr/bin/env python
import fileinput, re, sys
from unidecode import unidecode # to install, run: $ pip install unidecode

for line in fileinput.input(inplace='--inplace' in sys.argv):
    print re.sub(r'<U([0-9A-F]{4})>',
                 lambda m: unidecode(unichr(int(m.group(1), 16))),
                 line),

它接受来自标准输入和/或命令行给出的文件的输入。

$ u9999-to-ascii data.in
LC_TIME
abday   "Dom";"Seg";/
    "Ter";"Qua";/
    "Qui";"Sex";/
    "Sab"

注意,由于 ascii 不支持,所以没有 á 字符,因此脚本将其替换为 ascii 模拟 a

如果你不需要ascii,那么:

#!/usr/bin/env python
from __future__ import print_function
import fileinput, re, sys

for line in fileinput.input(mode='rb', inplace='--inplace' in sys.argv):
    print(re.sub(br'<U([0-9A-F]{4})>', lambda m: br'\u'+m.group(1),
                 line).decode('raw-unicode-escape'), end='')

此脚本适用于 Python2.6+ 和 Python3.x。示例:

$ u9999-to-unicode.py data.in
LC_TIME
abday   "Dom";"Seg";/
    "Ter";"Qua";/
    "Qui";"Sex";/
    "Sáb"

注意,有á。如果您的终端编码不支持来自data.in 的所有Unicode 字符,此脚本可能会产生错误。在这种情况下,您可以使用 .encode() 方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-27
    • 1970-01-01
    • 2010-09-13
    • 1970-01-01
    相关资源
    最近更新 更多