【问题标题】:Faster method to operate on all characters in a text file对文本文件中的所有字符进行操作的更快方法
【发布时间】:2021-10-26 10:24:07
【问题描述】:

我想在一个很长的字符串上执行一个任务,我认为我需要单独对每个字符进行操作。在这样做之前,我首先尝试建立访问所有这些字符所需时间量的基线

我打算使用的输入是一个文本文件,其中一行包含一个单词,由数字组成。

下面的代码是我目前为止最好的尝试,有没有更快的方法来逐个访问字符串中的所有字符

function handle_split() {
    split=$1
    while read -N 1 char; do
        :
    done
}

while read -N 100000 split; do
    ((i=i%100)); ((i++==0)) && wait
    handle_split $split &
done < "filename.txt"

此行旨在避免同时排队超过 100 个任务

((i=i%100)); ((i++==0)) && wait

拆分的长度是根据输入的已知长度硬编码的,在这种情况下,我使用的是大约十亿个字符的字符串。


顺便说一句,我尝试将我的字符串拆分转换为数组以提高性能。

function handle_split() {
    split=($@)
    for char in ${split[@]}; do
        :
    done
}

while read -N 100 split; do
   arr=($(echo $split | grep -o .))
   ((i=i%100)); ((i++==0)) && wait
   handle_split "${arr[@]}" &
done < "filename.txt"

但是这种使用数组的实现甚至比简单的 for 循环还要慢。

while read -N 1 char; do
    :
done < "filename.txt"

【问题讨论】:

  • 在您的情况下,Shell 循环将是一场灾难。它们不适用于那么多迭代。如果您稍微解释一下您对每个字符的处理方式,我们可能可以使用sedawkpythonperl 或其他方式提出更有效的方法。例如,它可以制成表格吗?我的意思是,如果你的字符是数字,而你只是想将它们转换成另一个字符集,tr 可能就是你需要的。
  • 我正在尝试将“15487355732..”之类的输入排序为“111222333..”之类的内容,虽然我可以通过排序来做到这一点,但我认为可以通过创建一个更快地完成每个数字出现次数的哈希图,处理过程是将存储在适当键中的值加 1,可能会将每个任务的结果转储到单独的文件中。
  • 另外,我知道循环方法不是最好的,我只是想确保我尝试了它的最佳实现,所以我可以说“所以,这是最好的这可以通过循环来完成,但还不够好,所以任何使用循环的东西都可以被丢弃”。
  • 您可以以任何可能的方式组织事物,使用 shell 命令处理数十亿位数字将花费大量时间。找出哪种实现的灾难性较小仅具有理论上的意义。

标签: string bash loops text text-files


【解决方案1】:

GNU 或 BSD awk 在这里可能是一个不错的选择:

awk 'BEGIN {FS=""}
  {for(i=1;i<=NF;i++) a[$i]++}
  END {for(i=0;i<10;i++) for(j=0;j<a[i];j++) printf "%d", i}' file

应该是关于你想要什么。说明:BEGIN 块中的FS="" 表示文件中的每个单个字符都是一个单独的字段。 {for(i=1;i&lt;=NF;i++) a[$i]++} 块循环遍历所有字段(1 到 NF),并为每个字段递增 a 数组的 10 个单元之一。 END 块在 END 处执行,它打印每个数字 d 的次数与 a[d] 一样多,按数字递增的顺序。

请注意,这不会打印最后的换行符。如果您需要在块末尾添加; print ""

END {for(i=0;i<10;i++) for(j=0;j<a[i];j++) printf "%d", i; print ""}

当然,结果是如此多余,以至于您可能更喜欢更紧凑的形式,例如,每个字符一行有两个字段:字符和出现次数:

awk 'BEGIN {FS=""}
  {for(i=1;i<=NF;i++) a[$i]++}
  END {for(i in a) printf "%s %d\n", i, a[i]}' file

刚刚在具有 1.4GB 输入的 3.6 GHz Intel Core i7 上测试了最后一个:2m38.480s。

【讨论】:

  • 这需要 GNU awk。
  • 好点,谢谢,添加了这个。请注意,它也适用于 BSD awk
  • 我对 awk 脚本进行了计时(没有 END 块),结果比我编写的多处理脚本略快,同时使用的系统资源要少得多,所以我认为我可以接受这个作为答案。关于排序位,这仍然太慢了一两个数量级。
  • 我有点惊讶它只是“稍微”快了一点。您是否在非常大的输入文件上对此进行了测试?如果你能负担得起 100 个进程,为什么不将你的输入分成尽可能多的块并 awk 并行呢?合并结果应该是微不足道的,尤其是紧凑的输出版本。
  • 你是对的,并行化 awk 脚本非常快,我可以用它来解决排序问题。在我的机器上,每个文件的拆分和处理(不合并结果)在 10^9 字符串上花费的时间不到 90 秒。
猜你喜欢
  • 2021-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-05
  • 2010-10-29
  • 2013-01-27
  • 1970-01-01
  • 2016-12-24
相关资源
最近更新 更多