如何使用sed处理Unicode字符

4
function change() {
  for i in {0..28}
  do
    echo ",${cryp_data_letter[$i]}" "${org_data[$i]}"
    sed -i "s/,${cryp_data_letter[$i]}/${org_data[$i]}/g" "./temp.txt"
    #cat "./temp.txt"
  done
}

我有一个函数用于按照特定规则更改temp.txt中的某些字符,但某些字符(如ı、ğ、ö等)会转换为空字符串。我认为问题的原因是UTF-8编码,所以我该如何使用Unicode应用sed?或者是否有其他建议来代替这个命令:"sed -i "s/,${cryp_data_letter[$i]}/${org_data[$i]}/g" "./temp.txt""?
这是给出的文件temp.txt:
abc ğhıi
def
jkl
oöpr
uü vy z
çgm ns
şt

并输出:

IDK ,ğS,ıT
NMY
BOÜ
G,öHÇ
P,ü ÖF ,
,çUŞ ZĞ
,şV

顺便提一下,在返回过程中,我将把所有字母改为小写,并在所有字母之前加上“,”,这样它就会变成sed之前的形式:
,a,b,c ,ğ,h,ı,i
,d,e,f
,j,k,l
,o,ö,p,r
,u,ü ,v,y ,z
,ç,g,m ,n,s
,ş,t

本地化:

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

1
你能创建一个MCVE吗?例如,你可以将printf 'Hello,\xC3\xB6\n' > file; sed -i -e $'s/,\xC3\xB6/, world/' file; cat file复制粘贴到你的终端并运行它,看看UTF-8的是否成功被替换为, world。你能否提供一些我们可以复制粘贴到终端的代码来展示你的意思?(你发布的代码没有声明cryp_data_letter或创建temp.txt,所以其他人无法尝试调试) - that other guy
抱歉我之前的解释,我已经添加了文件内容,并且cryp_data_letter是一个包含29个字母的数组。@thatotherguy - user11250514
同时,org_data 是 cryp_data_letter 的不同顺序,它们的大小是相同的。@thatotherguy - user11250514
请在你的问题中添加 locale 的输出。 - Cyrus
我添加了它 @Cyrus - user11250514
你的 temp.txt 文件只包含小写字符,而你的数据数组却是大写的。默认情况下,sed 区分大小写。 - that other guy
2个回答

4

这里存在多个问题,每个问题单独或组合起来都可能导致你的问题。

  • 我们无法知道你使用哪种字符集和编码。你的语言环境已正确设置为UTF-8,但你的终端和其他软件可能无法正确地进行交互操作。也许可以参考Stack Overflow character-encoding 标签信息页面了解一些背景和诊断信息。
  • 即使你的系统和工具通常支持UTF-8,也不能保证你的sed支持。许多sed变体仍然对Unicode视而不见,并且没有稳定的建议来确定确切的行为。有时候切换到其他语言是有意义的;许多简单的sed脚本可以很容易地移植到perl -CSD -p下运行,几乎不需要任何更改。
  • 即使一切正常,Unicode提供了多种表示许多带重音的字符的方式。如果你的数据将ö表示为单个代码点U+00E6,但你的脚本包含相应的分解序列,或者反之,则你的sed脚本(可能)无法替换替代表示。请查找Unicode规范化。

解决以上问题后,如果第二个问题足够解决,下面的方法可能会起作用。

perl -CSD -pi~ e 'tr/AEİR...FJ/ABCÇ...YZ/' ./temp.txt

请注意使用-i~选项进行原地编辑并保存备份文件。我不太有信心这将毫无问题地起作用,需要您进行一些修改,并且可能需要澄清。


真的不是特意要检查社区维基,但算了吧 :-) - tripleee

1
抱歉无法回答您的问题,但我无法复现您的问题。
以下是您的代码完整的自包含脚本(下次请自行完成):
#!/bin/bash

if [[ ö != $'\xC3\xB6' ]]
then
  echo "You didn't save this file as UTF-8"
  exit 1
fi

function change() {
  for i in {0..28}
  do
#    echo ",${cryp_data_letter[$i]}" "${org_data[$i]}"
    sed -i "s/,${cryp_data_letter[$i]}/${org_data[$i]}/g" "./temp.txt"
    #cat "./temp.txt"
  done
}

# Shift all characters one letter ahead in the alphabet
cryp_data_letter=({a..z} ğ ö ı)
org_data=({b..z} ğ ö ı a)

# Create the file as you say it is before the sed
cat > temp.txt << "EOF"
,a,b,c ,ğ,h,ı,i
,d,e,f
,j,k,l
,o,ö,p,r
,u,ü ,v,y ,z
,ç,g,m ,n,s
,ş,t
EOF

change

cat temp.txt

当我运行./testscript时,我会得到以下输出:
bcd öiaj
efg
klm
pıqs
v,ü wz ğ
,çhn ot
,şu

正如您所看到的,包括 öğ 在内的字母被成功替换和插入。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接