텍스트 파일에서 문자열을 분리하는 빠른 방법?


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.. 껍질만으로도 충분히 빨리 느껴집니다 ..
heemayl

솔직히 빠르지는 않습니다. 여전히 오랜 시간이 걸립니다. 나는 리눅스 / 프로그래밍에 익숙하지 않다. 그래서 쉘을 사용하는 것뿐만 아니라 더 빠른 방법이 있다고 생각한다면 아이디어에 개방적이다.
user3891532

4
시도하십시오 { while read l<&3; do head -c"$l"; echo; done 3<lengths.txt; } <String.txt.
jimmij

@jimmij, 그 답을 고수하는 것은
어떻습니까

답변:


7

넌 할 수있어

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

설명이 필요합니다.

주요 아이디어는 사용 { head ; } <file하는 것이며 과소 평가 된 @mikeserv 답변 에서 파생됩니다 . 그러나이 경우 우리는 많은 heads 를 사용해야 하므로 두 파일 (입력 할 파일 의 주요 파일과 옵션 의 인수로 줄)에서 입력 while을 전달하기 위해 루프가 도입되고 파일 설명자와 약간 조정됩니다. . 아이디어는 속도가 좋아질 때 마다 명령을 받거나 호출 할 때마다 탐색 할 필요가 없다는 것입니다 . 는 각 반복 후 개행 문자를 인쇄 할 만하다.headString.txtlength.txt-cString.txtheadcutecho

얼마나 빠르며 (있는 경우) >Entry_i줄 사이에 추가 하는 것이 운동으로 남습니다.


I / O 리디렉션을 깔끔하게 사용하십시오. 태그가 리눅스이기 때문에, 당신은 합리적으로 쉘이 강타하고 사용하는 것입니다 가정 할 수 있습니다 read -u 3설명 (3)에서 읽기
조나단 레플러

@JonathanLeffler, Linux는 거의 관련이 없습니다 bash. 대부분의 Linux 기반 시스템은 bash설치되어 있지 않습니다 (Android 및 기타 임베디드 시스템 생각). bash배쉬로 전환, 모두의 느린 쉘되는 것입니다 가능성이 열화 성능을 더 크게보다 작은 이득이로 전환 read <&3하는 read -u3(어떤 경우에 외부 명령 등의 운영 비용에 비해 크지 않을 것이다 가지고 힘 head). head내장 된 (및 비표준 -c옵션 을 지원하는) ksh93으로 전환 하면 성능이 훨씬 향상됩니다.
Stéphane Chazelas

head -c( head비표준 옵션을 사용할 수 있는 구현의 경우) 의 인수는 문자가 아닌 바이트 수입니다. 그것은 멀티 바이트 로케일에 차이를 만들 것입니다.
Stéphane Chazelas

7

일반적으로 쉘 루프를 사용하여 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한 번에 1 바이트 (또는 일반 파일의 경우 몇 바이트)를 읽는 쉘 명령 보다 훨씬 효율적으로 버퍼링 ). 두 파일을 한 번만 (메모리에 완전히 저장하지 않고) 읽습니다. 쉘 루프에서 외부 명령을 실행하는 솔루션보다 몇 배 더 효율적입니다.

( -C이 숫자가 바이트 수와 달리 현재 로케일의 문자 수 여야하는 경우 옵션을 추가하십시오 . 샘플과 같은 ASCII 문자의 경우 아무런 차이가 없습니다.)


이는 $_출력 및 입력 매개 변수를 모두에 대한 복잡한 재사용 read이지만 스크립트의 바이트 수를 줄입니다.
Jonathan Leffler

빠른 테스트 (OP의 샘플이 100,000 회 반복 됨) 에서이 솔루션은 @jimmij의 것 보다 약 1200 배 빠릅니다 (0.3 초 ​​대 6 분 (의 bash경우 16 초 PATH=/opt/ast/bin:$PATH ksh93)).
Stéphane 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유일한 기능을 선호 합니다 FIELDWIDTHS:awk -vFIELDWIDTHS="$(tr '\n' ' ' < lengths.txt)" '{for(i=1;i<=NF;i++)print">Entry"i ORS$i}' string.txt
manatwork
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.