【问题标题】:Script to convert ASCII chars to "<Uxxx>" unicode notation将 ASCII 字符转换为“<Uxxx>”Unicode 表示法的脚本
【发布时间】:2011-07-28 12:45:11
【问题描述】:

我正在对 Linux 语言环境文件 /usr/share/i18n/locales(如 pt_BR)进行一些更改,并且要求格式字符串(如 %d-%m-%Y %H:%M)必须以 Unicode 格式指定,其中每个(在本例中为 ASCII)字符表示为&lt;U00xx&gt;

所以这样的文字:

LC_TIME
d_t_fmt "%a %d %b %Y %T %Z"
d_fmt   "%d-%m-%Y"
t_fmt   "%T"

必须是:

LC_TIME
d_t_fmt "<U0025><U0061><U0020><U0025><U0064><U0020><U0025><U0062><U0020><U0025><U0059><U0020><U0025><U0054><U0020><U0025><U005A>"
d_fmt   "<U0025><U0064><U002D><U0025><U006D><U002D><U0025><U0059>"
t_fmt   "<U0025><U0054>"

因此,我需要一个命令行脚本(无论是 bash、Python、Perl 还是其他),它会接受像 %d-%m-%Y 这样的输入并将其转换为 &lt;U0025&gt;&lt;U0064&gt;&lt;U002D&gt;&lt;U0025&gt;&lt;U006D&gt;&lt;U002D&gt;&lt;U0025&gt;&lt;U0059&gt;

输入字符串中的所有字符都是 ASCII 字符(从 0x200x7F),所以这实际上是一种更高级的“字符到十六进制字符串”转换。

谁能帮帮我?我在 bash 脚本方面的技能非常有限,在 Python 方面更差。

优雅、解释性解决方案的奖励。

谢谢!

(顺便说一句,这将是我的previous question 的“反向”脚本)

【问题讨论】:

    标签: python string bash ascii


    【解决方案1】:

    使用 Python

    #!/usr/bin/env python3.2
    import sys
    text = sys.argv[1]
    encoded = "".join("<U{0:04X}>".format(ord(char)) for char in text)
    print(encoded)
    

    用法:

    $ python3 file.py "enter_input"
    <U0065><U006E><U0074><U0065><U0072><U005F><U0069><U006E><U0070><U0075><U0074>
    

    (相同的脚本应该适用于 python 3.x 和 2.x。只需在 shebang 中更改版本 到你拥有的那个。)

    解释:

    1. 我们需要导入the sys module 来读取命令行参数。

    2. sys.argv list 是所有命令行参数的列表。条目 [0] 是程序名称,条目 [1] 是第一个参数,依此类推。

    3. f(char) for char in text 是一个generator expression。它将循环遍历text 变量中的每个字符,然后对其应用函数f,最后将结果收集为惰性列表(iterable)。

    4. ord(char) 查找字符的 Unicode 代码点。

    5. "&lt;U{0:04X}&gt;".format(x) 是一种字符串格式化方法,如名称所述。格式化字符串接受1个输入x,格式化成04X format,意思是前导零,宽度4,大写十六进制。

    6. "".join(it) 连接惰性列表(可迭代)it 中的所有元素。 "" 表示分隔符是一个空字符串。

    7. print(encoded) 将字符串encoded 写入标准输出。

    【讨论】:

    • +1 获取详细说明。多么棒的植物素课程啊! :) 我马上测试一下。
    • 它不起作用,与shebang有关:/usr/bin/env: python 3.2: 没有这样的文件或目录。从“python3.2”更改为“python”也会出错(使用 aaa 测试时)。我应该使用字符串还是文件名?可以更改为接受命令行参数吗?喜欢encode "aaaa"
    • @MestreLion:可能你还没有安装python3.2。尝试改成python3.1,或者最后使用Python 2.x版本。
    • @KennyTM:虽然是一个不错的 python 脚本,但我将不得不 -1 这个因为它不会产生 OP 请求的输出。您的单方面将 每个 char 转换为 unicode。
    • 好吧,既然它已修复,它确实会产生我想要的东西。我想这次我的问题没有很好地表述。我只是想提供小字符串(“”只是为了分隔它,并允许输入中的空格,但这不是强制性的)并且脚本会转换它的每个字符。这样,我可以通过复制并粘贴我的修改,将我之前的问题文件中使用的日期和货币格式更改为自定义格式。
    【解决方案2】:

    文件输入的每个字符

    如果您想将文件的 每个 字符转换为 unicode 表示,那么它就是这个简单的单行

    while IFS= read -r -n1 c;do printf "<U%04X>" "'$c"; done < ./infile
    

    STDIN 上的每个字符

    如果你想制作一个类似 unix 的工具,将 STDIN 上的输入转换为类似 unicode 的输出,那么使用这个:

    uni(){ c=$(cat); for((i=0;i<${#c};i++)); do printf "<U%04X>" "'${c:i:1}"; done; }
    

    概念证明

    $ echo "abc" | uni
    <U0061><U0062><U0063>
    

    只有双引号之间的字符

    #!/bin/bash
    
    flag=0
    while IFS= read -r -n1 c; do
        if [[ "$c" == '"' ]]; then
            ((flag^=1))
            printf "%c" "$c"
        elif [[ "$c" == $'\0' ]]; then
            echo
        elif ((flag)); then
            printf "<U%04X>" "'$c"
        else
            printf "%c" "$c"
        fi
    done < /path/to/infile
    

    概念证明

    $ cat ./unime
    LC_TIME
    d_t_fmt "%a %d %b %Y %T %Z"
    d_fmt   "%d-%m-%Y"
    t_fmt   "%T"
    abday "Dom";"Seg";/
    here is a string with "multiline
    quotes";/
    
    $ ./uni.sh
    LC_TIME
    d_t_fmt "<U0025><U0061><U0020><U0025><U0064><U0020><U0025><U0062><U0020><U0025><U0059><U0020><U0025><U0054><U0020><U0025><U005A>"
    d_fmt   "<U0025><U0064><U002D><U0025><U006D><U002D><U0025><U0059>"
    t_fmt   "<U0025><U0054>"
    abday "<U0044><U006F><U006D>";"<U0053><U0065><U0067>";/
    here is a string with "<U006D><U0075><U006C><U0074><U0069><U006C><U0069><U006E><U0065>
    <U0071><U0075><U006F><U0074><U0065><U0073>";/
    

    说明

    真的很简单

    1. while IFS= read -r -n1 c;:一次迭代输入一个字符(通过-n1)并将字符存储在变量c 中。 IFS=-r 标志在那里,因此 read 内置函数不会分别尝试进行分词或解释转义序列。
    2. if [[ "$c" == '"' ]];: 如果当前字符是双引号
    3. ((flag^=1)): 将 flag 的值从 0->1 或 1->0 取反
    4. elif [[ "$c" == $'\0' ]];: 如果当前 char 是 NUL,则 echo 换行
    5. elif ((flag)):如果flag为1,则进行unicode音译
    6. printf "&lt;U%04X&gt;" "'$c": 进行 unicode 音译的魔法。请注意,$c 之前的单引号是强制性的,因为它告诉 printf 我们正在给它一个数字的 ASCII 表示形式。
    7. else printf "%c" "$c": 打印出不执行 unicode 音译的字符

    【讨论】:

    • 哇,您实际上设法转换了“”之间的文本!这比我需要的要多,酷!单线“转换每个字符”是我所要求的。 +1 以获得详细解释,并提供比要求更多的内容。我现在都测试一下
    • 我对您的第一个完整解决方案很好奇,所以我先对其进行了测试,效果很好。 2个小问题:它与abday "Dom";"Seg";/之类的行有点混淆,将所有内容从第一个“转换到行尾,除了”本身(但包括;和;/)。此外,它没有转换行“多行”字符串,其中关闭“是 2(或 3)行。不,不要费心去解决这个问题,因为多行字符串不是问题的一部分,而且解决这个问题的正则表达式会很远超出范围。但我们将不胜感激修复aaaa "bbb";"ccc";/ 模式。
    • @MestreLion 你不能用正则表达式解决嵌套引号。见HERE。但我可以(并且将)解决非多行、非嵌套问题
    • @MestreLion:“它永远不会捕捉到那些”,我相信我的新版本确实捕捉到了那些。请参阅新的概念证明文本。这不是你认为的多行吗?
    • @MestreLion: 是的,只有当你知道标志将高达1 时才有效,否则你需要做((flag=!flag))。如果您还不知道,bash 也可以使用其他运算符执行 set to self 快捷方式。
    【解决方案3】:

    echo -n "aä" | ruby -KU -e '$&lt;.chars{|c| print "&lt;U"+"%04X"%c.unpack("U*")[0]+"&gt;"}; puts'

    输出&lt;U0061&gt;&lt;U00E4&gt;

    -KU = $KCODE = "U"

    【讨论】:

      【解决方案4】:

      Shell脚本解决方案:

      #!/bin/sh
      
      while IFS= read -r -n1 c;
          do printf "<U%04X>" "'$c";
      done
      

      这会读取标准输入并打印到标准输出(假设您已将脚本放入可执行文件 toUnicode.sh):

      > echo "hello" | toUnicode.sh
      <U0068><U0065><U006C><U006C><U006F><U0000>
      

      这确实会打印 EOF 字符(&lt;U0000&gt;),但您可以更改此脚本以满足您的需要,无论您是想一次读取一行输入,还是修剪它或以另一种方式更改它。

      【讨论】:

      • 这似乎与公认的答案第一解决方案相同
      • 我对@9​​87654321@ 投了赞成票,但是工具示例的介绍(“STDIN 上的每个字符”)让我很困惑。我试图制作一个通用工具,虽然答案提供了一些指导,但我花了很多时间试图确定回答者为什么以这种方式展示他的工具。我想在我所经历的困惑中拯救任何处于我地位的人。
      猜你喜欢
      • 2015-05-08
      • 2018-03-21
      • 2018-11-19
      • 2018-11-18
      • 2013-01-26
      • 2012-11-18
      • 1970-01-01
      • 2013-05-10
      • 1970-01-01
      相关资源
      最近更新 更多