【问题标题】:Split large string into substrings将大字符串拆分为子字符串
【发布时间】:2011-11-25 22:56:19
【问题描述】:

我有一个很大的字符串,比如

ABCDEFGHIJKLM...

我想以这种方式将其拆分为长度为 5 的子字符串:

>1
ABCDE
>2
BCDEF
>3
CDEFG
[...]

【问题讨论】:

    标签: string bash shell


    【解决方案1】:
    ${string:position:length}
    

    $string 中提取$length 子字符串的字符 $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 的 Advanced Bash-Scripting Guide 中的 Manipulating Strings

    然后使用循环遍历,在位置上加1,提取每个长度为5的子串。

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

    【讨论】:

      【解决方案2】:

      fold -w5 应该可以解决问题。

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

      干杯!

      【讨论】:

        【解决方案3】:

        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,其余的没有打印出来
        • 你的 awk 版本是什么? (awk --version) 它应该与 gawk 一起使用。尝试用 gawk 替换 awk。或者 nawk 如果你在 Sun unix 机器上。
        • 谢谢,gawk 工作正常,但是我的大字符串仍然很慢 - 但我正在努力加快速度
        【解决方案4】:

        ...或使用split 命令:

        $ ls
        
        $ echo "abcdefghijklmnopqr" | split -b5
        
        $ ls
        xaa  xab  xac  xad
        
        $ cat xaa
        abcde
        

        split 也对文件进行操作...

        【讨论】:

          【解决方案5】:

          在 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
          

          【讨论】:

            【解决方案6】:

            sed 会这样做吗?:

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

            【讨论】:

            • sed 's/...../&amp;\n/g' filename 足够了,但不能解决问题(还需要理解\n,这不是所有sed 实现都可以做到的)。
            【解决方案7】:
            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:这取决于您实际在做什么,以及您是否要处理每个集合、找到特定的偏移量,或者您有什么。你的目标是什么?
            【解决方案8】:

            sed 可以做到:

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

            【讨论】:

              【解决方案9】:

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

              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
              

              【讨论】:

                【解决方案10】:

                感谢你们,我能够找到一种快速完成此任务的方法!这是我结合了这里的一些想法的解决方案:

                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 "^$"
                

                [上述答案最初是添加到问题本身中的。以下是相关的 cmets。]

                您的splitfive 可能会更有效率。无需管道切割,在 bash 中您可以说 cut -c "$2"- &lt;&lt;&lt;"$1" | sed 等,它会稍微好一些。 -- sorpigal 2011 年 9 月 28 日 11:48

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

                【讨论】:

                • 这实际上不会产生预期的结果。问题是 ABCDE, BCDEF... 但这会产生 ABCDE, FGHIJ...
                猜你喜欢
                • 1970-01-01
                • 2015-12-28
                • 2019-05-22
                • 2015-09-01
                • 1970-01-01
                • 1970-01-01
                • 2014-09-26
                • 2015-01-16
                相关资源
                最近更新 更多