【问题标题】:Wrapping hunspell to stem a large number of words efficiently?包装 hunspell 以有效地阻止大量单词?
【发布时间】:2015-10-28 06:59:03
【问题描述】:

我编写了一个用于提取英语单词的脚本,它做得不错,但是当我在大文件上使用它时需要很长时间,这些文件有 1000 多个单词,每行一个。有没有办法加快速度?也许完全不同的方法?不同的编程语言?不同的词干?

file=$1
while read -r a
do
b="$(echo "$a" | hunspell -s -d en_US | wc -l)"
if [[ "$b" -eq 2 ]]
 then
   g="$(echo "$a" | hunspell -s -d en_US | wc -w)"
   if [[ "$g" -eq 1 ]]
    then
     echo "$a" | hunspell -s -d en_US | awk 'FNR==1 {print $1}'
    else
     echo "$a" | hunspell -s -d en_US | awk 'FNR==1 {print $2}'
   fi
 else
   if [[ "$a" == *ing ]] || [[ "$a" == *ed ]]
     then
       echo "$a" | hunspell -s -d en_US | awk 'FNR==2 {print $2}'
     else
       echo "$a" | hunspell -s -d en_US | awk 'FNR==1 {print $1}'
   fi
fi
done < "$file" 

这是一个例子。

输入文件

cliché
womb
range
strain
fiddle
coup
earnest
touched
gave
dazzling
blindfolded
stagger
buying
insignia

输出

cliché
womb
range
strain
fiddle
coup
earnest
touch
give
dazzle
blindfold
stagger
buy
insignia

工作原理

如果你运行hunspell -s -d en_US word,它可以根据一个词给你不同的结果。选项和要采取的措施如下:

  • 一行一字(打印那个字)
  • 一行两个字(打印第二个字)
  • 两行两字;以“ing”或“ed”结尾(在第二行打印第二个单词)
  • 两行两字;不以“ing”或“ed”结尾(在第一行打印第一个单词)

【问题讨论】:

  • 不要重复自己。您对每个单词运行 echo "$a" | hunspell -s -d en_US 两到三遍。不要那样做。只需执行一次并保存即可。 bash 在运行这样的大文件方面也不是最快的。使用其他东西(甚至只是 awk)可能会快得多。如果hunspell 可以在您可以可靠读取的流模式下运行,那么您可以做到这一点。
  • 不是ifs 拖慢了你的脚本;这是您在条件中放入的所有子shell。
  • 您可以在运行一个echo "$a" | hunspell -s -d en_US | awk 'FNR==1 {print $1}' 所需的执行时间内运行if [[ "$a" == *ing ]] || [[ "$a" == *ed ]]; then :; fi 一千次。管道很昂贵;旋转外部工具很昂贵!学习使用本机 bash 字符串操作。
  • 另外,如果此代码有效并且您只需要帮助使其变得更好,那么这个问题比这个网站更适合codereview.stackexchange.com
  • ...顺便说一句,这个脚本中的大多数分支打算处理什么情况是非常不明显的。评论会很有帮助,或者至少选择测试数据以实际执行所有提供的逻辑。

标签: bash stemming hunspell


【解决方案1】:

以下内容发出完全相同的输出(但将gave 更改为give,我的hunspell 似乎没有在它的字典中)——而且快得多:

last_word=; stems=( )
while read -r word stem _; do
  if [[ $word ]]; then
    last_word=$word
    [[ $stem ]] && stems+=( "$stem" )
  else
    if (( ${#stems[@]} == 0 )); then
      printf '%s\n' "$last_word"        # no stems available; print input word
    elif (( ${#stems[@]} == 1 )); then
      printf '%s\n' "${stems[0]}"       # found one stem; print it.
    else
      case $last_word in
        *ing|*ed) printf '%s\n' "${stems[1]}" ;; # "ing" or "ed": print the 2nd stem
        *)        printf '%s\n' "${stems[0]}" ;; # otherwise: print the 1st stem
      esac
    fi
    stems=( )
  fi
done < <(hunspell -s -d en_US <"$1")

请注意,hunspell 整个文件只运行一次,而不是每个单词一次;它一遍又一遍地重新启动hunspell,与 bash 没有任何关系,你的脚本一直在花费时间。

【讨论】:

  • 如何运行它?第 5 行:文件:没有这样的文件或目录
  • file 是您的输入文件的名称。如果你愿意,可以$1
  • 由于一旦循环退出,while 循环不会设置 shell 所需的任何变量,因此将hunspell 的输出通过管道传输到循环可能更简单,而不是重定向来自进程的输入替换。
  • 它给出了不同的结果。抱歉,我的帖子最后有点误导,它是一列输入文件,输出也是一列。
  • 是的,这确实具有误导性。然后只需echo "$first_root"——或者,更好的是printf '%s\n' "$first_root"
猜你喜欢
  • 1970-01-01
  • 2017-10-01
  • 2021-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多