有没有一种方法可以完全匹配某些Unicode范围。
让我们以西里尔字母范围为例:U + 400至U + 52f
整个字符范围可以用以下命令打印(从bash或zsh):
$ echo -e $(printf '\\U%x' $(seq 0x400 0x52f)) ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁ҂҃҄҇ҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӏӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹӺӻӼӽӾӿԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԐԑԒԓԔԕԖԗԘԙԚԛԜԝԞԟԠԡԢԣԤԥԦԧԨԩԪԫԬԭԮԯ
$ a=$(zsh -c 'echo -e $(printf '\''\\U%x'\'' $(seq 0x400 0x52f))')
要过滤某个范围,请使用0x452至0x490,这是预期的输出:
$ b=$(bash -c 'echo -e $(printf '\''\\U%x'\'' $(seq 0x452 0x490))')
$ echo "$b"
ђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁ҂҃҄҇ҊҋҌҍҎҏҐ
$ echo "$b" | xxd
00000000: d192 d193 d194 d195 d196 d197 d198 d199 ................
00000010: d19a d19b d19c d19d d19e d19f d1a0 d1a1 ................
00000020: d1a2 d1a3 d1a4 d1a5 d1a6 d1a7 d1a8 d1a9 ................
00000030: d1aa d1ab d1ac d1ad d1ae d1af d1b0 d1b1 ................
00000040: d1b2 d1b3 d1b4 d1b5 d1b6 d1b7 d1b8 d1b9 ................
00000050: d1ba d1bb d1bc d1bd d1be d1bf d280 d281 ................
00000060: d282 d283 d284 d285 d286 d287 d288 d289 ................
00000070: d28a d28b d28c d28d d28e d28f d290 0a ...............
但是用sed过滤似乎是不可能的。这不起作用:
$ echo "$a" | sed 's/[^\x452-\x490]//g'
也不行(结果与其他字符匹配(可能是整理问题)):
$ echo "$a" | sed $'s/[^\u452-\u490]//g' АБВГжзийклмнопрстуфхцчшщъыьэюяёђєѕіїјљњћќѝўџҋҍҏҐҗҙқҝҟҡңҥҧҩҫҭүұҳҵҷҹһҽҿӂӄӆӈӊӌӎӐӒӔӝӟӡӣӥӧөӫӭӯӱӳӵӹԅԇԉԋԍԏ
甚至没有(相同的整理问题):
$ echo "$a" | sed 's/[^ђ-Ґ]//g'
与awk一起工作:
$ echo "$a" | awk '{gsub(/[^ђ-Ґ]/,"")}1'
但是使用十六进制范围的唯一方法是使用shell将十六进制转换为Unicode字符
$ echo "$a" | awk $'{gsub(/[^\u452-\u490]/,"")}1'
或(两种解决方案):
$ c=$(bash -c 'printf "\u452-\u490"')
$ echo "$a" | awk '{gsub(/[^'"$c"']/,"")}1'
$ echo $a | awk -v ra="[^$c]" '{gsub(ra,"")}1'
问题:
如果没有更高的外壳,awk可以用十六进制数执行。
如果可能的话,与sed一起使用的整理序列所匹配的范围是多少sed 's/[^ђ-Ґ]//g'
?
PS:谢谢,我知道可以在perl中完成。
在基本sed中,括号表达式中的范围遵循Posix。在Posix中,括号表达式中的范围遵循排序规则。排序顺序被定义为仅在C语言环境中基于字符数字值。但仅适用于单字节值。其余语言环境在Posix中未定义。
要使范围在sed括号表达式中起作用,我们需要使用按数字Unicode代码点(即C.UTF-8)排序的排序规则。但这创建了对utf8中的范围字符进行编码的第二个要求:
获取Unicode代码点范围的字符八进制表示形式(如果使用的语言环境是utf-8):
$ printf '\u452\u490' | od -An -to1
如果不在utf-8语言环境中,请将值转换为utf-8:
$ printf '\u452\u490' | iconv -t utf-8 | od -An -to1
321 222 322 220
添加破折号和\ o以使其在较旧/现在的sed中工作:
$ printf '\o%s\o%s-\o%s\o%s' $(printf '\u452\u490'|iconv -tutf-8|od -An -to1)
\o321\o222-\o322\o220
使用该范围可用于sed:
$ echo "$a" | LC_ALL=C.UTF-8 sed 's/[^\o321\o222-\o322\o220]//g'
但是请确保语言环境为C.UTF-8,并且给定的字符串在utf8中进行了编码,并转换回使用中的语言环境:
$ echo "$a" | iconv -t utf-8 |
LC_ALL=C.UTF-8 sed 's/[^\o321\o222-\o322\o220]//g' |
iconv -f utf-8
请注意,上面我们使用了shell进行转换\u452\u490
。
给定十六进制Unicode代码点,GNU awk能够生成一个字符串(只要该语言环境有效地允许此类字符):
<<<"$a" awk 'BEGIN{for(i=0x452;i<=0x490;i++){r=r sprintf("%c", i)}}
{gsub("[^" range "]", "")}1'
如果当前语言环境在Unicode代码点编号处不包含那些Unicode代码点,则您需要转换为已知包含此类代码点的语言环境,并使用匹配的语言环境环境变量,例如:
<<<"$a" iconv -t utf8 |
LC_ALL=en_US.UTF-8 awk '
BEGIN{for(i=0x452;i<=0x490;i++){r=r sprintf("%c", i)}}
{gsub("[^" r "]", "")}1
' | iconv -f utf8
底线是需要一个更高的shell(GNU bash或zsh)或awk(仅GNU)。
或使用更高层次的语言(例如perl):
$ echo "$a" | perl -Mopen=locale -ane 's/[^\x{452}-\x{490}]//g; print'
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句