举个例子,根据维基词典,这是“when”的发音。 enPR、IPA 和 X-SAMPA 是显示发音的不同方案。
when:* {{a|US}} {{enPR|wĕn|hwĕn}}, {{IPA|/wɛn/|/ʍɛn/}}, {{X-SAMPA|/wEn/|/WEn/}}
我想提取关键字when
及其两个国际音标发音,并将它们放在不同的行上:
when wɛn
when ʍɛn
一个单词可能有一种、两种或更多种 IPA 发音,并且可能有也可能没有 enPR 或 X-SAMPA 发音。
我正在考虑 PHP,列表中的列表,但这似乎有点过分了,如果可能的话,我不希望用户必须安装它。有没有办法在 awk、sed、cut 或其他标准 Unix 命令行实用程序中执行此操作?
答案1
使用sed
,您可以将其写为:
sed '/\([^:]*\):.*{IPA|\([^}]*\).*/!d;s//\1 \2/;s,/,,g;:1
s/\(\([^ ]*\).*\)|/\1\n\2 /;t1'
分解(@slm,谢谢)
上面的命令可以分解如下:
解析输入
when: ... {IPA|...}
并删除不匹配的行。在
/pattern/!d; s//repl/
我们[d]丢弃与[!]模式不匹配的行,然后在下一个[s]替换命令中重用相同的模式(空模式意味着重用最后一个模式)。我们可以通过使用
b
而不是[d]删除不匹配的行而不是改变它们d
,或者如果我们知道所有行都与模式匹配,我们可以s/pattern/repl/
直接使用。/\([^:]*\):.*{IPA|\([^}]*\).*/
该模式将数据分成 2 块。第一个块是
when:
.这段代码\([^:]*\):
表示获取所有字符直到遇到 a:
并将其保存在 temp 中。多变的 (\1
)。之间的所有字符
:
(包括)都{IPA|
将被跳过。保存的下一位是IPA|
.这是通过代码块 来完成的,\([^}]*\)
它表示保存所有代码,直到}
遇到 a。这被保存在变量(\2
)中。笔记:任何时候
sed
你想保存一段字符串,都可以将它括在括号中。它们需要用 a 进行转义,\
以便sed
知道您的意思不是字面上的括号。就像这样:\( savethis \)
。例子
$ sed 's/\([^:]*\):.*{IPA|\([^}]*\).*/\1 \2/;' sample.txt when /wɛn/|/ʍɛn/
删除所有正斜杠 (
/
)这个看起来更复杂,因为它使用了备用分隔符。您通常会使用 形式
s///g
,但sed
让我们动态地组成分隔符,因此我们使用逗号 (s,,,g
) 代替。该块搜索它们/
并将其替换为空。例子
$ sed '/\([^:]*\):.*{IPA|\([^}]*\).*/!d;s//\1 \2/;s,/,,g;' sample.txt when wɛn|ʍɛn
迭代每个 IPA
:1 s/\(\([^ ]*\).*\)|/\1\n\2 /;t1
这是迄今为止该解决方案中最复杂的组件。很难看出发生了什么,但这个块是一个条件分支。
:label command(s) t label
标签是
:1
命令s/\(\([^ ]*\).*\)|/\1\n\2 /;
,是t label
“测试”,用于查看前一个命令是否修改了模式空间。如果是这样,则跳转到标签1
,因此t1
.循环内的命令
如果我们把它拿出
label ... loop
来一秒钟,然后增加我们的 IPA 示例,使其达到 3,您可以更好地看到发生了什么。{{IPA|/wɛn/|/ʍɛn/|/blah/}}
我们将使用之前的命令来完成此操作。
when wɛn|ʍɛn|blah
如果我们现在运行这个:
$ echo "when wɛn|ʍɛn|blah" | sed 's/\(\([^ ]*\).*\)|/\1 \2 /;'
我们得到这个:
when wɛn|ʍɛn when blah
你能看到它现在在做什么吗?是的,我也没有,所以让我们再简化一点,去掉换行符 (
\n
) 并换入一些较短的字符串。更简单的例子
$ echo "X C1|C2|C3" | sed 's/\(\([^ ]*\).*\)|/\1 \2 /;' X C1|C2 X C3
现在这里发生的事情是,代码
\(\([^ ]*\).*\)|
很聪明,因为它嵌套了括号,所以它们就像这样( ( ) )
。内部括号中匹配的内容不是空格。这是when
字符串。外括号匹配最后一个管道 (|
) 之前的所有内容。此代码片段的另一个有趣的事情是括号是有序的,以便外部的括号被存储,
\1
而内部的括号被存储\2
。这是因为sed
根据它们遇到的顺序对它们进行编号。\1
您可以通过使用附加的和来扩展该代码片段,从而让自己相信这一点\2
。$ echo "X C1|C2|C3" | sed 's/\(\([^ ]*\).*\)|/\1 \1 \1 /;' X C1|C2 X C1|C2 X C1|C2 C3 $ echo "X C1|C2|C3" | sed 's/\(\([^ ]*\).*\)|/\1 \2 \2 /;' X C1|C2 X X C
所以循环里面的命令基本上都是走
X
2次。一次作为整体的一部分X C1|C2
(括号外),第二次作为空间的任何部分(括号内)。返回条件分支
好的,所以分支基本上会调用 #5 中的命令,对于 IPA,其中有超过 2 个。
sed
的分支构造将继续重新运行该命令,直到该命令不再修改替换,此时它会停止。例子
$ echo "X C1|C2|C3" | sed ':1 s/\(\([^ ]*\).*\)|/\1\n\2 /; t1' X C1 X C2 X C3
希望以上内容能够帮助其他路人将来找到这个答案。
答案2
在 Perl 脚本中使用 Perl(处理STDIN
)
while(<>) {
if(/^([^:]+):.*{{IPA\|([^}]+)}}/) {
print "$1 $_\n" foreach(split /\|/, $2);
}
}
或在命令行(管道)上
perl -ne ' if(/^([^:]+):.*{{IPA\|([^}]+)}}/) { print "$1 $_\n" foreach(split /\|/, $2); }'
答案3
使用 bash 和 grep
line='when:* {{a|US}} {{enPR|wĕn|hwĕn}}, {{IPA|/wɛn/|/ʍɛn/}}, {{X-SAMPA|/wEn/|/WEn/}}'
IFS=$': \t' read -ra words <<< "$line"
for item in "${words[@]}"; do
if [[ $item == "{{IPA|"* ]]; then
grep -o '/[^/]\+/' <<< "$item" | while read -r pronunc; do
echo "${words[0]} ${pronunc//\//}"
done
fi
done