如何使用sed表达式替换单宽度

时间:2018-05-19 22:53:22

标签: regex ubuntu docker sed locale

我想使用sed表达式替换文件中找到的单宽度等值的某些双宽字符。这并不像预期的那样有效,但表达了我想要做的事情(这是在bash脚本中):我已经将字母数字范围与我可以想到的其他一些混合在一起,不确定是否需要将其分成两个不同的-e参数,基于if范围等。

sed -e 's,[0-9a-zA-Z()【】-一],[0-9a-zA-Z\(\)\[\]\-\-],g' ${file} > ${file}.cleaned

文件是tsv(制表符分隔值)文本文件。 根据{{​​1}}命令,类型为:file或(在另一种情况下)UTF-8 Unicode text, with CRLF line terminators

示例输入:

UTF-8 Unicode text, with no line terminators

示例输出:

Part Number
123-956-AA
343-213-【E】
XTE-898一(5)

我的系统是Ubuntu16.04,运行在我们的基础映像构建的Docker容器中,该映像是从Part Number 123-956-AA 343-213-[E] XTE-898-(5) 构建的,它具有phusion/passenger-ruby23:0.9.19的基本映像(最终到基础),shell是{{ 1}},sed版本为ubuntu:16.04GNU bash, version 4.3.46(1)-release (x86_64-pc-linux-gnu)命令的结果为:

sed (GNU sed) 4.2.2

更新

选择的解决方案/答案是1)使用locale命令(正如其他答案也建议),在我的情况下,2)设置如下所示的LL_ALL以避免我得到的错误LANG= LANGUAGE= LC_CTYPE="POSIX" LC_NUMERIC="POSIX" LC_TIME="POSIX" LC_COLLATE="POSIX" LC_MONETARY="POSIX" LC_MESSAGES="POSIX" LC_PAPER="POSIX" LC_NAME="POSIX" LC_ADDRESS="POSIX" LC_TELEPHONE="POSIX" LC_MEASUREMENT="POSIX" LC_IDENTIFICATION="POSIX" LC_ALL= 命令。确实该范围似乎不适用于y命令,因此必须单独识别所有字符(正如我之前错误地认为的那样)

y

更新2:

根据其他回答者的建议(一个神秘地消失了),为系统设置的语言环境被进一步调查为解决方案,而不是在命令行设置环境变量。由于这是一个Docker镜像容器环境,我找到了一个解决方案,可以放入我们的基本图像中,解决了基本系统级别的问题。

我已添加到我们的基础Dockerfile:

y

现在LC_ALL=en_US.UTF-8 sed 'y/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890()【】-一/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890()[]--' file.tsv命令生成;

# Set the locale
RUN locale-gen en_US.UTF-8
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8'

现在locale命令的工作原理如下:

LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_CTYPE="en_US.UTF-8" LC_NUMERIC="en_US.UTF-8" LC_TIME="en_US.UTF-8" LC_COLLATE="en_US.UTF-8" LC_MONETARY="en_US.UTF-8" LC_MESSAGES="en_US.UTF-8" LC_PAPER="en_US.UTF-8" LC_NAME="en_US.UTF-8" LC_ADDRESS="en_US.UTF-8" LC_TELEPHONE="en_US.UTF-8" LC_MEASUREMENT="en_US.UTF-8" LC_IDENTIFICATION="en_US.UTF-8" LC_ALL=en_US.UTF-8

作为旁注,我希望stackoverflow提供了一种方法来回答多个答案的答案,因为最初的3个答案(再次,一个消失)都让我得到了解决方案,但我不得不只选择一个。这经常发生。

2 个答案:

答案 0 :(得分:2)

如果perl没问题:

$ perl -Mopen=locale -Mutf8 -pe 'tr/0-9a-zA-Z()【】-一/0-9a-zA-Z()[]--/' ip.txt
Part Number
123-956-AA
343-213-[E]
XTE-898-(5)
  • -Mopen=locale -Mutf8将区域设置指定为utf8
  • tr/0-9a-zA-Z()【】-一/0-9a-zA-Z()[]--/根据需要翻译字符,也可以使用y代替tr


可以使用sed (GNU sed) 4.2.2,但它不支持范围

$ # simulating OP's POSIX locale
$ echo '91A9foo' | LC_ALL=C sed 'y/A9/A9/'
sed: -e expression #1, char 12: strings for `y' command are different lengths

$ # changing to a utf8 locale
$ echo '91A9foo' | LC_ALL=en_US.UTF-8 sed 'y/A9/A9/'
91A9foo

进一步阅读:https://wiki.archlinux.org/index.php/locale

答案 1 :(得分:1)

使用the y command

  

y/source-chars/dest-chars/

     

将模式空间中与任何 source-chars 匹配的任何字符与 dest-chars 中的相应字符进行音译。

     

示例:将“a-j”音译为“0-9”:

$ echo hello world | sed 'y/abcdefghij/0123456789/'
74llo worl3
     

/个字符可以被任何给定的y命令中的任何其他单个字符统一替换。)

     

source-chars dest中可能会显示/(或其他任何使用的字符),\或换行符的实例-chars 列表,规定每个实例都由\转义。 source-chars dest-chars 列表必须包含相同数量的字符(在解除转义后)。

     

请参阅GNU coreutils的tr命令以获得类似的功能。

请记住,你必须拼出每个角色,范围在这里不起作用。

所以:

sed -e 'y/0123456789abcdefgh[...]/0123456789abcdefgh[...]/'

我会让你拼出所有其他角色。