V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
helloiac
V2EX  ›  问与答

sed 正则替换不支持 4 字节生僻字,怎么办

  •  
  •   helloiac · 2018-07-03 00:22:20 +08:00 · 1548 次点击
    这是一个创建于 2371 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我要给一些古籍文本行加上包裹标签,如 p、h2 之类,是用 sed 做的;调试使用中发现,当行中包含 4 字节的生僻字时,.*这个匹配会失败,从而导致加包裹标签失败。

    下面的测试是希望把问题说清楚,实际下面的 sed 命令是一个较大的脚本中一个长管道中的一环。测试文本中的 4 字节生僻字是“𢢼”。

    请不吝赐教!

    注释:赋值测试字符串给变量 a
    qhel@qhel-vagrant MSYS /d/Documents/project/coding/guji
    $ a="公乃密遣解𢢼、高祚等乘險夜襲"
    
    注释:查看字符串长度
    qhel@qhel-vagrant MSYS /d/Documents/project/coding/guji
    $ echo ${#a}
    15
    
    注释:替换失败
    qhel@qhel-vagrant MSYS /d/Documents/project/coding/guji
    $ echo $a | sed "s/^[^#=;%:|-].*$/<p>&<\/p>/g";
    公乃密遣解𢢼、高祚等乘險夜襲
    
    =====
    注释:去掉生僻字重新赋值给 b
    qhel@qhel-vagrant MSYS /d/Documents/project/coding/guji
    $ b="公乃密遣解、高祚等乘險夜襲"
    
    qhel@qhel-vagrant MSYS /d/Documents/project/coding/guji
    $ echo ${#b}
    13
    
    注释:替换成功
    qhel@qhel-vagrant MSYS /d/Documents/project/coding/guji
    $ echo $b | sed "s/^[^#=;%:|-].*$/<p>&<\/p>/g";
    <p>公乃密遣解、高祚等乘險夜襲</p>
    
    
    11 条回复    2018-07-03 17:05:45 +08:00
    imn1
        1
    imn1  
       2018-07-03 01:59:10 +08:00
    现在没开 linux 机器,测试不了
    你试试把「𢢼」改成\u{2288c},试一下,注意需要花括号
    OscarUsingChen
        2
    OscarUsingChen  
       2018-07-03 04:23:56 +08:00
    嗯,这边没问题。可能是你系统编码的问题。

    a="公乃密遣解𢢼、高祚等乘險夜襲"

    echo ${#a}
    14

    echo $a | sed "s/^[^#=;%:|-].*$/<p>&<\/p>/g";
    <p>公乃密遣解𢢼、高祚等乘險夜襲</p>
    kokutou
        3
    kokutou  
       2018-07-03 07:18:00 +08:00 via Android
    我记得当时 Windows 下替换不了,后来把源文本文件转成 utf-8,然后 cmd 里运行一下 chcp 65001 再运行 sed
    应该是要文本编码和环境一样吧。
    helloiac
        4
    helloiac  
    OP
       2018-07-03 09:39:48 +08:00 via Android
    @imn1 我是在 Windows 做的,因为文本不定,手动替换不太可行,后面我在 Linux 也测一下反馈结果
    helloiac
        5
    helloiac  
    OP
       2018-07-03 09:42:36 +08:00 via Android
    @OscarUsingChen 我用的是 msys2 的环境,gbk ‘和 utf8 的终端环境都试过。你这用的是 Linux 吗?
    helloiac
        6
    helloiac  
    OP
       2018-07-03 09:46:31 +08:00 via Android
    @kokutou 谢谢!我脚本处理的文本是 utf8 的,这个在终端的测试文本编码应该就是和终端环境有关了,也是 utf8。我没用 cmd,用的是 msys2 的 mintty。
    helloiac
        7
    helloiac  
    OP
       2018-07-03 10:44:26 +08:00
    @imn1 试了一下是这样的,分别用复制粘贴和手动输入\u{2288c}:
    ```
    qhel@qhel-vagrant MSYS /D/develop/vagrant/ubuntu/soushu
    $ a="公乃密遣解𢢼▒、高祚等乘險夜襲"

    qhel@qhel-vagrant MSYS /D/develop/vagrant/ubuntu/soushu
    $ echo ${#a}
    16

    qhel@qhel-vagrant MSYS /D/develop/vagrant/ubuntu/soushu
    $ echo $a | sed "s/^[^#=;%:|-].*$/<p>&<\/p>/g";
    公乃密遣解𢢼▒、高祚等乘險夜襲

    qhel@qhel-vagrant MSYS /D/develop/vagrant/ubuntu/soushu
    $ a="公乃密遣解\u{2288c}、高祚等乘險夜襲"

    qhel@qhel-vagrant MSYS /D/develop/vagrant/ubuntu/soushu
    $ echo $a | sed "s/^[^#=;%:|-].*$/<p>&<\/p>/g";
    <p>公乃密遣解\u{2288c}、高祚等乘險夜襲</p>
    ```
    helloiac
        8
    helloiac  
    OP
       2018-07-03 10:45:20 +08:00
    @OscarUsingChen Linux 环境试过了,同样没问题。后面实在不行就麻烦一点,这个步骤搞到 Linux 执行。
    imn1
        9
    imn1  
       2018-07-03 13:13:50 +08:00   ❤️ 1
    这个问题的出现,主要是软件的 unicode 核心没有支持 FFFF 以后的字符( utf-8 四字节)
    linux bash4.1 好像还未支持,但 4.2+就支持了,我以为你在 4.1 测试

    你可以用「 unicode codepoint 」 google 一下,有绕弯的解决方案的
    helloiac
        10
    helloiac  
    OP
       2018-07-03 16:31:13 +08:00
    @imn1 谢谢!我比较了本地 msys2 环境和 vagrant 中的 bash 版本,分别是:
    GNU bash, version 4.4.19(2)-release (x86_64-pc-msys)
    GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

    版本都高于 4.1,可能是 msys 版本的 bash 的问题。正在升级 bash 和 msys 的系统,升级完了用新版本 bash 和 zsh 都试一下。
    helloiac
        11
    helloiac  
    OP
       2018-07-03 17:05:45 +08:00
    @imn1 升级 msys 的 bash 到最新版本后问题依旧,不过用 zsh 完美解决了问题。大谢!前面我还尝试过将本地的 LANG 变量修改为与 vagrant 中的 ubuntu 相同,不过没效果,没想到根本上是 shell 的问题。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2849 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 14:13 · PVG 22:13 · LAX 06:13 · JFK 09:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.