문제

나는 매우 긴 줄이 포함된 매우 큰 일반 텍스트 파일(10GB 이상, 예, 크기가 무엇인지에 따라 다르다는 것을 알고 있습니다)을 처리해야 합니다.

가장 최근 작업에는 다른 파일의 데이터를 기반으로 한 줄 편집이 포함되었습니다.

(수정해야 하는) 데이터 파일에는 1500000줄이 포함되어 있으며 각 줄은 다음과 같습니다.길이는 800자입니다.각 줄은 고유하며 하나의 식별 번호만 포함하며 각 식별 번호는 고유합니다)

수정자 파일은 예를 들어 다음과 같습니다.1800줄 길이이며 데이터 파일에서 수정해야 하는 ID 번호와 금액 및 날짜가 포함되어 있습니다.

방금 수정자 파일을 (Vim 정규식을 사용하여) sed로 변환했지만 매우 비효율적입니다.

데이터 파일에 다음과 같은 줄이 있다고 가정해 보겠습니다.

(some 500 character)id_number(some 300 character)

그리고 300자 부분의 데이터를 수정해야 합니다.

수정자 파일을 기반으로 다음과 같은 sed 줄을 생각해 냈습니다.

/id_number/ s/^\(.\{650\}\).\{20\}/\1CHANGED_AMOUNT_AND_DATA/

그래서 이렇게 1800개의 줄이 생겼습니다.

하지만 매우 빠른 서버에서도 내가 다음 작업을 수행하면

sed -i.bak -f modifier.sed data.file

모든 패턴 x 모든 라인을 읽어야 하기 때문에 매우 느립니다.

더 좋은 방법은 없을까요?

메모: 저는 프로그래머가 아니며 학교에서 알고리즘에 대해 배운 적이 없습니다.서버에서는 오래된 Perl 버전인 awk, sed를 사용할 수 있습니다.

도움이 되었습니까?

해결책

내가 제안하는 접근 방식(바람직한 순서)은 이 데이터를 다음과 같이 처리하는 것입니다.

  1. 데이터베이스(인덱스가 있는 간단한 SQLite 기반 DB라도 10GB 파일의 sed/awk보다 성능이 훨씬 뛰어남)
  2. 고정된 레코드 길이를 포함하는 플랫 파일
  3. 가변 레코드 길이를 포함하는 플랫 파일

데이터베이스를 사용하면 텍스트 파일 처리 속도를 늦추는 모든 작은 세부 사항(관심 있는 레코드 찾기, 데이터 수정, DB에 다시 저장)을 처리할 수 있습니다.Perl의 경우 DBD::SQLite를 살펴보세요.

플랫 파일을 계속 사용하려면 큰 파일과 함께 수동으로 색인을 유지 관리해야 조작해야 할 레코드 번호를 더 쉽게 찾을 수 있습니다.아니면 더 나은 방법은 아마도 귀하의 ID 번호일 것입니다. ~이다 당신의 기록적인 숫자는요?

가변 레코드 길이가 있는 경우 고정 레코드 길이로 변환하는 것이 좋습니다(귀하의 ID만 가변 길이로 표시되므로).그렇게 할 수 없다면 기존 데이터가 파일에서 이동하지 않을 수도 있습니다.그런 다음 이전에 언급한 색인을 유지하고 필요에 따라 새 항목을 추가할 수 있습니다. 차이점은 색인이 레코드 번호를 가리키는 대신 이제 파일의 절대 위치를 가리킨다는 것입니다.

다른 팁

나는 당신에게 perl로 작성된 프로그램을 제안합니다 (나는 sed/awk 전문가가 아니고 그들이 할 수있는 것이 무엇인지는 안 돼요).

"알고리즘"은 간단합니다. 우선 각 ID에 적용 할 새 데이터 문자열을 제공 할 수있는 해시 맵을 구성해야합니다. 이것은 물론 수정 자 파일을 읽는 것으로 달성됩니다.

이 hasmap에서 인구가 쌓이면 데이터 파일의 각 줄을 탐색하고 줄의 중간에 ID를 읽고 위에서 설명한대로 새 줄을 생성 할 수 있습니다.

나도 Perl Guru는 아니지만 프로그램이 매우 간단하다고 생각합니다. 글을 쓰는 데 도움이 필요하다면 :-)

Perl을 사용하면 특히 id_number의 너비가 일정한 경우 id_number를 얻으려면 substr을 사용해야합니다.

my $id_number=substr($str, 500, id_number_length);

그 후 $ id_number가 범위에 있으면 substr을 사용하여 나머지 텍스트를 대체해야합니다.

substr($str, -300,300, $new_text);

Perl의 정규 표현식은 매우 빠르지만이 경우에는 그렇지 않습니다.

내 제안은 데이터베이스를 사용하지 않는다는 것입니다. 잘 쓰여진 PERL 스크립트는 이러한 종류의 작업에서 크기로 데이터베이스를 능가합니다. 날 믿어, 나는 그것에 대한 많은 실용적인 경험이있다. Perl이 완료되면 데이터를 데이터베이스로 가져 오지 않았습니다.

800 숯으로 1500000 줄을 쓰면 1.2GB처럼 보입니다. 디스크 (30MB/s)가 매우 느리면 40 초 안에 읽습니다. 더 나은 50-> 24s, 100-> 12s 등. 그러나 2GHz CPU의 Perl Hash 조회 (DB 조인) 속도는 5Mlookups/s 이상입니다. 그것은 당신의 CPU 바운드 작업이 몇 초 안에 있고 당신은 IO 바운드 작업이 수십 초 안에 있음을 의미합니다. 실제로 10GB 숫자가 변경되지만 비율은 동일합니다.

데이터 수정 크기가 변경되는지 여부를 지정하지 않았습니다 (수정이 완료 될 수있는 경우) 따라서 우리는이를 가정하지 않고 필터로 작동합니다. "수정 자 파일"형식과 어떤 종류의 수정을 지정하지 않았습니다. 다음과 같은 것들에 의해 분리되었다고 가정합니다.

<id><tab><position_after_id><tab><amount><tab><data>

우리는 stdin의 데이터를 읽고 stdout에 쓸 것입니다. 스크립트는 다음과 같습니다.

my $modifier_filename = 'modifier_file.txt';

open my $mf, '<', $modifier_filename or die "Can't open '$modifier_filename': $!";
my %modifications;
while (<$mf>) {
   chomp;
   my ($id, $position, $amount, $data) = split /\t/;
   $modifications{$id} = [$position, $amount, $data];
}
close $mf;

# make matching regexp (use quotemeta to prevent regexp meaningful characters)
my $id_regexp = join '|', map quotemeta, keys %modifications;
$id_regexp = qr/($id_regexp)/;     # compile regexp

while (<>) {
  next unless m/$id_regexp/;
  next unless $modifications{$1};
  my ($position, $amount, $data) = @{$modifications{$1}};
  substr $_, $+[1] + $position, $amount, $data;
}
continue { print }

광산 노트북에서는 150 만 행, 1800 개의 조회 ID, 1.2GB 데이터에 대해 약 30 분이 걸립니다. 10GB의 경우 5 분이 넘지 않아야합니다. 당신에게 합리적입니까?

당신이 IO 바운드가 아니라고 생각하면 (예 : 일부 NAS를 사용하는 경우) CPU 묶음은 약간의 가독성과 변경을 희생 할 수 있습니다.

my $mod;
while (<>) {
  next unless m/$id_regexp/;
  $mod = $modifications{$1};
  next unless $mod;
  substr $_, $+[1] + $mod->[0], $mod->[1], $mod->[2];
}
continue { print }

거의 확실히 데이터베이스를 사용해야합니다 Mikeyb는 제안했다.

어떤 이유로 데이터베이스를 사용하지 않으려면, 수정 목록이 메모리에 맞는 경우 (현재 1800 줄에서) 가장 효율적인 방법은 제안한 수정으로 채워진 해시 가능입니다. Yves Baumes.

수정 목록조차도 큰 지점에 도달하면 ID로 두 파일을 모두 정렬 한 다음 수행해야합니다. 목록 병합 - 기본적으로 :

  1. 입력 파일의 "상단"에서 ID를 수정 파일의 "상단"에있는 ID와 비교합니다.
  2. 일치하는 경우 레코드를 그에 따라 조정하십시오
  3. 작성하십시오
  4. 파일에서 "상단"줄을 버리고 (알파벳순 또는 숫자) 가장 낮은 ID를 가진 사람이 해당 파일에서 다른 줄을 읽습니다.
  5. goto 1.

무대 뒤에서 데이터베이스는 단일 SQL을 사용 하여이 변경을 수행하는 경우 거의 확실히 목록 병합을 사용합니다. UPDATE 명령.

sqlloader 또는 datadump 결정에 대한 좋은 거래. 그게 갈 길입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top