파일의 모든 행을 쌍으로 확장하는 “고양이”명령 줄 도구 케이스는이 출력을 awk와 같은 다른 명령으로 스트리밍

다음과 같은 파일 (sample.txt라고 함)이 있다고 가정합니다.

Row1,10
Row2,20
Row3,30
Row4,40

본질적으로 네 행 모두의 페어 단위 조합 인이 파일의 스트림에서 작업 할 수 있기를 원합니다 (따라서 총 16 개로 끝나야합니다). 예를 들어, 출력이 다음과 같은 스트리밍 (즉, 효율적인) 명령을 찾고 있습니다.

Row1,10 Row1,10
Row1,10 Row2,20
Row1,10 Row3,30
Row1,10 Row4,40
Row2,20 Row1,10
Row1,20 Row2,20
...
Row4,40 Row4,40

내 유스 케이스는이 출력을 awk와 같은 다른 명령으로 스트리밍 하여이 쌍 조합에 대한 메트릭을 계산하려고한다는 것입니다.

나는 이것을 awk에서 할 수있는 방법이 있지만 내 관심사는 END {} 블록을 사용한다는 것은 기본적으로 출력하기 전에 전체 파일을 메모리에 저장한다는 것을 의미합니다. 예제 코드 :

awk '{arr[$1]=$1} END{for (a in arr){ for (a2 in arr) { print arr[a] " " arr[a2]}}}' samples/rows.txt
Row3,30 Row3,30
Row3,30 Row4,40
Row3,30 Row1,10
Row3,30 Row2,20
Row4,40 Row3,30
Row4,40 Row4,40
Row4,40 Row1,10
Row4,40 Row2,20
Row1,10 Row3,30
Row1,10 Row4,40
Row1,10 Row1,10
Row1,10 Row2,20
Row2,20 Row3,30
Row2,20 Row4,40
Row2,20 Row1,10
Row2,20 Row2,20

본질적으로 파일을 메모리에 저장 한 다음 END 블록으로 출력 할 필요없이 효율적인 스트리밍 방법이 있습니까?



답변

전체 파일을 배열에 저장할 필요가 없도록 awk로 수행하는 방법은 다음과 같습니다. 이것은 기본적으로 terdon과 동일한 알고리즘입니다.

원하는 경우 명령 줄에 여러 파일 이름을 지정할 수도 있으며 각 파일을 독립적으로 처리하여 결과를 연결합니다.

#!/usr/bin/awk -f

#Cartesian product of records

{
    file = FILENAME
    while ((getline line <file) > 0)
        print $0, line
    close(file)
}

내 시스템에서 이것은 terdon의 perl 솔루션 시간의 약 2/3에서 실행됩니다.


답변

나는 확실히이 메모리에하는 것보다 더 나은,하지만하지있어 sed하는 r파이프의 반대편에 모든 그것의 INFILE의 라인과 다른에 대한 INFILE 밖으로 EADS는 교류 H입력 라인 오래된 공간 …

cat <<\IN >/tmp/tmp
Row1,10
Row2,20
Row3,30
Row4,40
IN

</tmp/tmp sed -e 'i\
' -e 'r /tmp/tmp' |
sed -n '/./!n;h;N;/\n$/D;G;s/\n/ /;P;D'

산출

Row1,10 Row1,10
Row1,10 Row2,20
Row1,10 Row3,30
Row1,10 Row4,40
Row2,20 Row1,10
Row2,20 Row2,20
Row2,20 Row3,30
Row2,20 Row4,40
Row3,30 Row1,10
Row3,30 Row2,20
Row3,30 Row3,30
Row3,30 Row4,40
Row4,40 Row1,10
Row4,40 Row2,20
Row4,40 Row3,30
Row4,40 Row4,40

나는 다른 방법으로 이것을했다. 그것은 저장 않는 몇 가지 : 그것은 문자열 같은 저장 – 메모리를

"$1" -

… 파일의 각 줄마다.

pairs(){ [ -e "$1" ] || return
    set -- "$1" "$(IFS=0 n=
        case "${0%sh*}" in (ya|*s) n=-1;; (mk|po) n=+1;;esac
        printf '"$1" - %s' $(printf "%.$(($(wc -l <"$1")$n))d" 0))"
    eval "cat -- $2 </dev/null | paste -d ' \n' -- $2"
}

매우 빠릅니다. 그것은 cat‘는에 파일의 행이 있기 때문에 여러 번 파일을이야 |pipe. 파이프의 다른 쪽에서 해당 입력은 파일에 줄이있는 횟수만큼 파일 자체와 병합됩니다.

case– 물건은 휴대 성입니다 yashzsh동안, 분할에 모두 추가 한 요소 mkshposh모두 잃게 한. ksh, dash, busybox, 및 bash인쇄로 제로 있기 때문에 많은 분야와 완전히 밖으로 모든 분할 printf. 위와 같이 위에서 언급 한 모든 쉘에 대해 동일한 결과를 내 컴퓨터에서 렌더링합니다.

파일이 매우 길면 $ARGMAX너무 많은 인수에 문제 가있을 수 있으며이 경우 소개 xargs하거나 비슷 해야합니다 .

출력 전에 동일한 입력이 주어지면 동일합니다. 하지만 내가 더 크게 가려면 …

seq 10 10 10000 | nl -s, >/tmp/tmp

그러면 이전에 사용한 것과 거의 동일한 파일 (sans ‘Row’)이 생성 되지만 1000 줄입니다. 얼마나 빠른지 직접 확인할 수 있습니다.

time pairs /tmp/tmp |wc -l

1000000
pairs /tmp/tmp  0.20s user 0.07s system 110% cpu 0.239 total
wc -l  0.05s user 0.03s system 32% cpu 0.238 total

1000 줄에서 쉘 사이의 성능에 약간의 변화가 있습니다- bash항상 느리지 만-어쨌든 그들이하는 유일한 작업은 인수 문자열 (1000 사본 filename -)을 생성하는 것이므로 효과는 최소화됩니다. zsh위와 같이 성능의 차이 bash는 100 초입니다.

길이가 다른 파일에 사용할 수있는 다른 버전은 다음과 같습니다.

pairs2()( [ -e "$1" ] || exit
    rpt() until [ "$((n+=1))" -gt "$1" ]
          do printf %s\\n "$2"
          done
    [ -n "${1##*/*}" ] || cd -P -- "${1%/*}" || exit
    : & set -- "$1" "/tmp/pairs$!.ln" "$(wc -l <"$1")"
    ln -s "$PWD/${1##*/}" "$2" || exit
    n=0 rpt "$3" "$2" | xargs cat | { exec 3<&0
    n=0 rpt "$3" p | sed -nf - "$2" | paste - /dev/fd/3
    }; rm "$2"
)

/tmp이상한 파일 이름에 걸리지 않도록 세미 임의 이름 으로 첫 번째 인수에 대한 소프트 링크를 만듭니다 . cat의 인수는를 통해 파이프를 통해 공급 되기 때문에 중요 xargs합니다. cat의 출력은 파일에 행이있는 횟수만큼 첫 번째 인수의 모든 행 <&3sed p헹구는 동안 저장되며 스크립트는 파이프를 통해 해당 행에 공급됩니다. 다시 paste입력을 병합하지만 이번에 -는 표준 입력과 링크 이름에 대해 두 개의 인수 만 다시 사용합니다 /dev/fd/3.

마지막- /dev/fd/[num]링크는 모든 리눅스 시스템에서 작동해야하며, 그 외에도 많은 이름의 파이프를 만들지 않고 사용하면 명명 된 파이프를 mkfifo사용 하지 않아도 작동합니다.

마지막으로 rm종료하기 전에 생성하는 소프트 링크입니다.

이 버전은 실제로 내 시스템에서 여전히 더 빠릅니다 . 더 많은 응용 프로그램을 실행하더라도 인수를 즉시 전달하기 시작하지만 먼저 모든 응용 프로그램을 쌓기 때문입니다.

time pairs2 /tmp/tmp | wc -l

1000000
pairs2 /tmp/tmp  0.30s user 0.09s system 178% cpu 0.218 total
wc -l  0.03s user 0.02s system 26% cpu 0.218 total

답변

글쎄, 당신은 항상 쉘에서 할 수 있습니다 :

while read i; do
    while read k; do echo "$i $k"; done < sample.txt
done < sample.txt 

awk솔루션 보다 느리게 처리 됩니다 (내 컴퓨터에서는 ~ 1000 초 동안 ~ 11 초가 걸리고 ~ 0.3 초가 소요됩니다 awk).하지만 적어도 두 줄 이상을 메모리에 보유하지 마십시오.

위의 루프는 예제에있는 매우 간단한 데이터에 적용됩니다. 백 슬래시를 질식시키고 후행 및 선행 공간을 먹습니다. 같은 것의보다 강력한 버전은 다음과 같습니다.

while IFS= read -r i; do
    while IFS= read -r k; do printf "%s %s\n" "$i" "$k"; done < sample.txt
done < sample.txt 

또 다른 선택은 perl대신 사용하는 것입니다.

perl -lne '$line1=$_; open(A,"sample.txt");
           while($line2=<A>){printf "$line1 $line2"} close(A)' sample.txt

위의 스크립트는 입력 파일 ( -ln) 의 각 줄을 읽고로 저장하고 다시 $lsample.txt다음와 함께 각 줄을 인쇄합니다 $l. 결과는 모두 쌍으로 조합되는 반면 2 줄만 메모리에 저장됩니다. 내 시스템에서는 0.61000 줄에서 약 초가 걸렸습니다 .


답변

zsh:

a=(
Row1,10
Row2,20
Row3,30
Row4,40
)
printf '%s\n' $^a' '$^a

$^a배열에서 배열에 대해 중괄호와 같은 확장을 설정합니다 (에서처럼 {elt1,elt2}).


답변

컴파일 할 수 있습니다 코드를 매우 빠른 결과를 얻을 수 있습니다.
1000 줄 파일에서 약 0.19-0.27 초 안에 완료됩니다.

현재 10000메모리에 줄을 읽습니다 (화면으로 인쇄 속도를 높이기 위해).1000 줄 당 문자10mb 메모리 보다 적게 사용 하여 문제가되지 않을 것이라고 생각합니다. 그래도 해당 섹션을 완전히 제거하고 문제가 발생하면 화면에 직접 인쇄하십시오.

파일을 저장할 파일의 이름은 g++ -o "NAME" "NAME.cpp"
어디에 있고이 코드가 저장된 파일은 어디에 사용하여 컴파일 할 수 있습니다NAMENAME.cpp

CTEST.cpp:

#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>
#include <cstdlib>
#include <sstream>
int main(int argc,char *argv[])
{

        if(argc != 2)
        {
                printf("You must provide at least one argument\n"); // Make                                                                                                                      sure only one arg
                exit(0);
   }
std::ifstream file(argv[1]),file2(argv[1]);
std::string line,line2;
std::stringstream ss;
int x=0;

while (file.good()){
    file2.clear();
    file2.seekg (0, file2.beg);
    getline(file, line);
    if(file.good()){
        while ( file2.good() ){
            getline(file2, line2);
            if(file2.good())
            ss << line <<" "<<line2 << "\n";
            x++;
            if(x==10000){
                    std::cout << ss.rdbuf();
                    ss.clear();
                    ss.str(std::string());
            }
    }
    }
}
std::cout << ss.rdbuf();
ss.clear();
ss.str(std::string());
}

데모

$ g++ -o "Stream.exe" "CTEST.cpp"
$ seq 10 10 10000 | nl -s, > testfile
$ time ./Stream.exe testfile | wc -l
1000000

real    0m0.243s
user    0m0.210s
sys     0m0.033s

답변

join -j 2 file.txt file.txt | cut -c 2-
  • 존재하지 않는 필드로 연결하고 첫 번째 공백을 제거하십시오.

필드 2는 file.txt의 모든 요소에 대해 비어 있고 동일하므로 join각 요소를 다른 모든 요소와 연결합니다. 실제로 카티 전 곱을 계산합니다.


답변

파이썬의 한 가지 옵션은 은 파일 메모리 매핑 하고 Python 정규식 라이브러리가 메모리 매핑 된 파일과 직접 작동 할 수 있다는 사실을 이용하는 것입니다. 파일에 대해 중첩 루프를 실행하는 것처럼 보이지만 메모리 매핑을 통해 OS에서 사용 가능한 물리적 RAM을 최적으로 재생합니다.

import mmap
import re
with open('test.file', 'rt') as f1, open('test.file') as f2:
    with mmap.mmap(f1.fileno(), 0, flags=mmap.MAP_SHARED, access=mmap.ACCESS_READ) as m1,\
        mmap.mmap(f2.fileno(), 0, flags=mmap.MAP_SHARED, access=mmap.ACCESS_READ) as m2:
        for line1 in re.finditer(b'.*?\n', m1):
            for line2 in re.finditer(b'.*?\n', m2):
                print('{} {}'.format(line1.group().decode().rstrip(),
                    line2.group().decode().rstrip()))
            m2.seek(0)

메모리 효율성이 여전히 문제가 될 수 있지만 Python의 빠른 솔루션

from itertools import product
with open('test.file') as f:
    for a, b  in product(f, repeat=2):
        print('{} {}'.format(a.rstrip(), b.rstrip()))
Row1,10 Row1,10
Row1,10 Row2,20
Row1,10 Row3,30
Row1,10 Row4,40
Row2,20 Row1,10
Row2,20 Row2,20
Row2,20 Row3,30
Row2,20 Row4,40
Row3,30 Row1,10
Row3,30 Row2,20
Row3,30 Row3,30
Row3,30 Row4,40
Row4,40 Row1,10
Row4,40 Row2,20
Row4,40 Row3,30
Row4,40 Row4,40