将大字符串分割成子字符串

18

我有一个类似于这样的大字符串

ABCDEFGHIJKLM...

我想要将它按照长度为5的子字符串进行拆��,方法如下:

>1
ABCDE
>2
BCDEF
>3
CDEFG
[...]
10个回答

29
${string:position:length}

Extracts $length characters of substring from $string at $position.

stringZ=abcABC123ABCabc
#       0123456789.....
#       0-based indexing.

echo ${stringZ:0}          # abcABC123ABCabc
echo ${stringZ:1}          # bcABC123ABCabc
echo ${stringZ:7}          # 23ABCabc

echo ${stringZ:7:3}        # 23A
                           # Three characters of substring.

-- 来自Mendel Cooper的《高级Bash脚本指南中的操作字符串章节

然后使用循环来逐个添加1到位置,以提取长度为5的每个子字符串。

end=$(( ${#stringZ} - 5 ))
for i in $(seq 0 $end); do
    echo ${stringZ:$i:5}
done

25

fold -w5应该就能解决问题。

$ echo "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | fold -w5
ABCDE
FGHIJ
KLMNO
PQRST
UVWXY
Z

干杯!


18

sed 可以一次完成:

$ echo "abcdefghijklmnopqr"|sed -r 's/(.{5})/\1 /g'
abcde fghij klmno pqr

或者

取决于你的需求:

$ echo "abcdefghijklmnopqr"|sed -r 's/(.{5})/\1\n/g' 
abcde
fghij
klmno
pqr

更新

我一开始以为这只是一个简单的字符串分割问题,没有仔细阅读问题。现在它应该会给你需要的东西:

这次仍然是一次性完成,但这次使用了awk:

$ echo "abcdefghijklmnopqr"|awk '{while(length($0)>=5){print substr($0,1,5);gsub(/^./,"")}}'

abcde
bcdef
cdefg
defgh
efghi
fghij
ghijk
hijkl
ijklm
jklmn
klmno
lmnop
mnopq
nopqr

嗯,我不知道为什么,但我只得到了abcde,其余的没有被打印出来。 - didymos
你的 awk 版本是多少?(awk --version)它应该能够与 gawk 兼容。尝试将 awk 替换为 gawk。如果你使用的是 Sun Unix 系统,则可以考虑使用 nawk。 - Kent
谢谢,使用 gawk 已经可以正常工作了,但是对于我的大字符串来说仍然太慢了 - 但我正在努力加速它。 - didymos

2

...或使用split命令:

$ ls

$ echo "abcdefghijklmnopqr" | split -b5

$ ls
xaa  xab  xac  xad

$ cat xaa
abcde

split 也可用于文件操作...


2

在bash中:

s=ABCDEFGHIJ
for (( i=0; i < ${#s}-4; i++ )); do 
  printf ">%d\n%s\n" $((i+1)) ${s:$i:5}
done

输出

>1
ABCDE
>2
BCDEF
>3
CDEFG
>4
DEFGH
>5
EFGHI
>6
FGHIJ

1
str=ABCDEFGHIJKLM
splitfive(){ echo "${1:$2:5}" ; }
for (( i=0 ; i < ${#str} ; i++ )) ; do splitfive "$str" $i ; done

或者,也许你想对结果进行更智能的处理

#!/usr/bin/env bash

splitstr(){
    printf '%s\n' "${1:$2:$3}"
}

n=$1
offset=$2

declare -a by_fives

while IFS= read -r str ; do
    for (( i=0 ; i < ${#str} ; i++ )) ; do
            by_fives=("${by_fives[@]}" "$(splitstr "$str" $i $n)")
    done
done

echo ${by_fives[$offset]}

然后调用它

$ split-by 5 2 <<<"ABCDEFGHIJKLM"
CDEFG

你可以从那里进行调整。

编辑:C语言中的简单版本,用于性能比较:

#include <stdio.h>

int main(void){
    FILE* f;
    int n=0;
    char five[6];

    five[5] = '\0';

    f = fopen("inputfile", "r");

    if(f!=0){
            fread(&five, sizeof(char), 5, f);
            while(!feof(f)){
                    printf("%s\n", five);
                    fseek(f, ++n, SEEK_SET);

                    fread(&five, sizeof(char), 5, f);
            }
    }

    return 0;
}

很抱歉我的 C 语言不好,我真的不懂这门语言。


谢谢!你的第一个想法是可以的,但对于我的目的来说太慢了...我有一个非常巨大的字符串-10^8个字符...所以将它分成子字符串需要很长时间... - didymos
@didymos:这取决于你实际要做什么,以及你是想处理每个集合,查找特定的偏移量,还是其他目标。你的目标是什么? - sorpigal

1

sed可以做到:

 sed -nr ':a;h;s/(.{5}).*/\1/p;g;s/.//;ta;' <<<"ABCDEFGHIJKLM" | # split string
     sed '=' | sed '1~2s/^/>/' # add line numbers and insert '>'

1

能用sed来做吗?:

$ sed 's/\(.....\)/\1\n/g' < filecontaininghugestring

1
sed 's/...../&\n/g' filename 虽然足够简单,但并不能解决问题(而且还需要理解 \n,而不是所有的 sed 实现都能做到)。 - sorpigal

0

多亏了你们,我能够快速找到解决方法!这是我的解决方案,结合了这里的几个想法:

str="ABCDEFGHIJKLMNOP"   
splitfive(){
    echo $1 | cut -c $2- | sed -r 's/(.{5})/\1\n/g'
}  
for (( i=0; i <= 5; i++ )); do
    splitfive "$str" $i
done | grep -v "^$"

[上面的答案最初添加到问题本身。以下是相关评论。]

你的splitfive可能更有效率。在bash中,你可以说cut -c "$2"- <<<"$1" | sed等等,它会稍微好一点。-- sorpigal 2011年9月28日11:48

你的sed表达式也可以改进为sed 's/...../&\n/g',执行速度大约快两倍。-- sorpigal 2011年9月28日11:56


这实际上并没有产生期望的结果。问题中说是 ABCDEBCDEF... 但这却产生了 ABCDEFGHIJ... - wjandrea

0
你可以使用cut命令并指定字符而不是字段,然后将输出分隔符更改为您需要的任何内容,例如换行符:

echo "ABCDEFGHIJKLMNOP" | cut --output-delimiter=$'\n' -c1-5,6-10,11-15

输出

ABCDE
FGHIJ
KLMNO

或者

echo "ABCDEFGHIJKLMNOP" | cut --output-delimiter=$':' -c1-5,6-10,11-15 

输出

ABCDE:FGHIJ:KLMNO

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接