从文本文件中拆分字符串的快速方法?


11

我有两个文本文件:string.txt和lengths.txt

String.txt:

abcdefghijklmnopqrstuvwxyz

lengths.txt

5
4
10
7

我想获取文件

>Entry_1
abcde
>Entry_2
fghi
>Entry_3
jklmnopqrs
>Entry_4
tuvwxyz

我正在处理大约28,000个条目,它们在200至56,000个字符之间变化。

目前,我正在使用:

start=1
end=0
i=0
while read read_l
do
    let i=i+1
    let end=end+read_l
    echo -e ">Entry_$i" >>outfile.txt
    echo "$(cut -c$start-$end String.txt)" >>outfile.txt
    let start=start+read_l
    echo $i
done <lengths.txt

但这效率很低。还有更好的主意吗?


如何str="$(cat string.txt)"; i=0; while read j; do echo "${file:$i:$j}"; i=$((i+j)); done <length.txt..seems不够快只能通过外壳为完成..
heemayl

老实说,速度并不快。它仍然需要相当长的时间。我对linux /编程非常陌生,因此,如果您认为有一种不仅使用shell的更快捷的方法,那么我也乐于接受。
user3891532

4
尝试{ while read l<&3; do head -c"$l"; echo; done 3<lengths.txt; } <String.txt
jimmij

@jimmij,该如何回答
iruvar

Answers:


7

你可以做

{
  while read l<&3; do
    {
      head -c"$l"
      echo
    } 3<&-
  done 3<lengths.txt
} <String.txt

它需要一些解释:

主要思想是使用,{ head ; } <file并从被低估的@mikeserv 答案派生而来。但是,在这种情况下,我们需要使用许多heads,因此while引入了循环,并对文件描述符进行了一些微调,以便传递到head两个文件的输入(文件String.txt作为要处理的主文件,以及从行length.txt作为-c选项的参数) 。想法是,速度的好处应该来自于不需要String.txt每次调用类似head或被cut调用的命令。该echo只是每次迭代后打印换行符。

快多少(如果有)并在行>Entry_i之间添加多少作为练习。


整洁地使用I / O重定向。由于标签是Linux,你可以合理假设的shell是bash和使用read -u 3从描述符3.阅读
乔纳森·莱弗勒

@ JonathanLeffler,Linux与无关bash。绝大多数基于Linux的系统尚未bash安装(请考虑使用Android和其他嵌入式系统)。bash是所有的最慢的外壳,切换到bash将可能降低性能超过显著的小的收益,从切换read <&3read -u3可能带来的(这在任何情况下都会比运行一个外部命令一样的成本是微不足道的head)。切换到head内置的ksh93 (以及支持非标准-c选项的ksh93 )将大大提高性能。
斯特凡Chazelas

请注意,head -c(对于使用该head非标准选项的实现,)的参数是字节数,而不是字符数。这将在多字节语言环境中有所作为。
斯特凡Chazelas

7

通常,您不想使用shell循环来处理text。在这里,我会使用perl

$ perl -lpe 'read STDIN,$_,$_; print ">Entry_" . ++$n' lengths.txt < string.txt
>Entry_1
abcde
>Entry_2
fghi
>Entry_3
jklmnopqrs
>Entry_4
tuvwxyz

那是一个命令,它读一次(与缓冲相比read,一次读一个字节(对于常规文件是几个字节)的shell 命令效率高得多),两个文件都读一次(而不是将它们全部存储在内存中),所以与在Shell循环中运行外部命令的解决方案相比,效率将提高几个数量级。

-C如果这些数字应该是当前语言环境中的字符数而不是字节数,请添加选项。对于示例中的ASCII字符,这没有任何区别)。


这是$_作为输出参数和输入参数到的繁琐重用read,但它减少了脚本中的字节数。
乔纳森·莱夫勒

在快速测试中(OP的样本重复了100000次),我发现此解决方案的速度是@jimmij的 1200倍(0.3秒vs 6分钟(带有bash,带有16秒PATH=/opt/ast/bin:$PATH ksh93))。
斯特凡Chazelas

6

bash,版本4

mapfile -t lengths <lengths.txt
string=$(< String.txt)
i=0 
n=0
for len in "${lengths[@]}"; do
    echo ">Entry_$((++n))"
    echo "${string:i:len}"
    ((i+=len))
done

输出

>Entry_1
abcde
>Entry_2
fghi
>Entry_3
jklmnopqrs
>Entry_4
tuvwxyz

4

awk

创建一个process.awk使用以下代码的文件:

function idx(i1, v1, i2, v2)
{
     # numerical index comparison, ascending order
     return (i1 - i2)
}
FNR==NR { a[FNR]=$0; next }
{ i=1;PROCINFO["sorted_in"] = "idx";
        for (j in a) {
                print ">Entry"j;
                ms=substr($0, i,a[j])
                print ms
                i=i+length(ms)
        }
}

保存并执行 awk -f process.awk lengths.txt string.txt


根据使用情况PROCINFO,这不是标准awk,而是gawk。在这种情况下,我希望使用另一个gawk功能FIELDWIDTHSawk -vFIELDWIDTHS="$(tr '\n' ' ' < lengths.txt)" '{for(i=1;i<=NF;i++)print">Entry"i ORS$i}' string.txt
manatwork
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.