설명하다:

설명하다:

유닉스의 긴 열에 한 줄에 하나의 값을 곱하고 0.01초 간격으로 증가하는 큰 파일이 있습니다. 하루 분량의 데이터에 대해 이는 864만 행에 해당합니다.

135699840000
135699840001
135699840002
135699840003
135699840004

이 파일의 각 줄에서 각 줄의 일련 날짜 번호(참조 연도 01/01/0000에 대한 matlab의 날짜 카운터)를 계산하는 명령을 실행하고 싶습니다.

735235.0000000000
735235.0000001157
735235.0000002314
735235.0000003472
735235.0000004629

저는 코딩이 처음이지만 while 루프를 사용하여 작동하도록 만들었습니다. 그러나 이는 매우 비효율적이며 실행하는 데 몇 시간이 걸립니다.

while read epochtimerange; do
echo "scale=10; (($epochtimerange/(100*86400))+719529)" |bc
done < epochtimerangetmp.txt > serialdaterangetmp.txt

awk를 사용하여 실행할 수 있는 방법이 있어야 한다고 생각하지만 작동시킬 수 없습니다. 중요한 것은 출력에서 ​​소수점 이하 10자리의 정밀도를 유지할 수 있다는 것입니다.

누구든지 나를 도와줄 수 있나요? 감사해요.

답변1

우리 모두 알고 있듯이 쉘은 매우 느립니다.
당신이 요구하는 것은 다음과 같이 쉘에서 달성될 수 있습니다:

#!/bin/bash
while read line; do
    bc <<<"scale=10;($line/(100*86400))+719529"
done <datafile

1000행을 처리하는 데 약 1.1초가 소요됩니다.
864만 장의 사진은 모두 약 2시간 41분이 소요됩니다.

또한 bc에 대한 수치 결과는 올바르게 반올림되지 않습니다.
예제의 5개 행은 다음 값을 생성합니다.

735235.0000000000
735235.0000001157
735235.0000002314
735235.0000003472
735235.0000004629

더 많은 숫자를 보려면 정밀도를 20으로 변경해 보겠습니다.

735235.00000000000000000000
735235.00000011574074074074
735235.00000023148148148148
735235.00000034722222222222
735235.00000046296296296296

예를 들어, 로 끝나는 세 번째 숫자 2314는 잘못 반올림되었으며 다음 숫자 는 로 반올림되어야 한다는 4것이 표시됩니다 .85

AWK

awk를 사용하면 더 빠른 솔루션을 얻을 수 있습니다. awk에서 요청한 내용을 구현하면 다음과 같습니다.

$ awk '{printf ("%.10f\n",($0/(100*86400))+719529)}' datafile

735235.0000000000
735235.0000001157
735235.0000002314
735235.0000003473
735235.0000004630

1000개 행을 처리하는 데는 0.006(6밀리초)밖에 걸리지 않습니다. 864만 개의 행을 모두 약 50초 안에 처리해야 합니다.
그러나 awk는 정밀도 범위를 초과했습니다. 기본적으로 64비트 부동 소수점 값을 사용하여 표현됩니다. 이는정밀도는 소수점 이하 15자리 정도입니다.. 데이터 결과의 정수 부분은 6자리이고, 분수 부분은 8번째 자리까지만 정확하게 추정할 수 있습니다.
실제로 비트 수를 확장하려고 하면 다음과 같습니다.

awk '{printf ("%.20f\n",($0/(100*86400))+719529)}' datafile

우리가 얻는 것은 소음뿐입니다.

735235.00000000000000000000
735235.00000011571682989597
735235.00000023143365979195
735235.00000034726690500975
735235.00000046298373490572

보다 정확한 bc 결과와 비교:

735235.00000000000000000000
735235.00000000000000000000

735235.00000011571682989597
735235.00000011574074074074

735235.00000023143365979195
735235.00000023148148148148

735235.00000034726690500975
735235.00000034722222222222

735235.00000046298373490572
735235.00000046296296296296

이 문제를 실제로 해결하려면 더 정확한 awk가 필요합니다.

다중 정밀도 AWK

GNU awk(여기서는 gawk라고 부르겠습니다)를 사용하고 MPFR(Multiple Precision Floating Point Library)로 컴파일하면 훨씬 더 높은 정밀도를 얻을 수 있습니다.

귀하의 awk에 이 라이브러리가 있는지 확인하십시오(버전을 문의하십시오):

$ awk --version
GNU Awk 4.1.3, API: 1.1 (GNU MPFR 3.1.5, GNU MP 6.1.1)
Copyright (C) 1989, 1991-2015 Free Software Foundation.

그리고 사용 가능한 정밀도를 사용하도록 awk 명령을 수정합니다.

gawk -M -v PREC=100 '{printf ("%.20f\n",($0/(100*86400))+719529)}' datafile

735235.00000000000000000000
735235.00000011574074074074
735235.00000023148148148148
735235.00000034722222222222
735235.00000046296296296296

결과는 고정밀도 bc와 동일합니다.
이 경우 awk의 속도와 bc의 정확도를 얻습니다.

십진수 10자리로 요청한 최종 명령은 다음과 같습니다.

gawk -M -v PREC=100 '{printf ("%.10f\n",($0/(100*86400))+719529)}' datafile

735235.0000000000
735235.0000001157
735235.0000002315
735235.0000003472
735235.0000004630

모든 값은 올바르게 반올림됩니다.

답변2

쉬운 방법: ex수정 라인을 사용하고 전체 버퍼(수정된 파일)를 에 전달합니다 bc. 그런 다음 수정된 버전을 인쇄합니다.

printf '%s\n' '%s:.*:&/8640000+719529:' 0a scale=10 . '%!bc' %p 'q!' | ex file.txt

샘플 파일의 출력:

735235.0000000000
735235.0000001157
735235.0000002314
735235.0000003472
735235.0000004629

또는 인쇄하는 대신 변경 사항을 저장하세요.

printf '%s\n' '%s:.*:&/8640000+719529:' 0a scale=10 . '%!bc' x | ex file.txt

설명하다:

ex전달 된 명령을 보려면 printf명령을 단독으로 실행하십시오.

$ printf '%s\n' '%s:.*:&/8640000+719529:' 0a scale=10 . '%!bc' %p 'q!'
%s:.*:&/8640000+719529:
0a
scale=10
.
%!bc
%p
q!

ex이제 그것들을 명령으로 나누어 보겠습니다. 첫 번째는 더 복잡하므로 특별히 설명 형식을 지정했습니다.

%s:.*:&/8640000+719529:
%  - For every line of the buffer (file)
 s  - Run a substitute command
  :  - Using ':' as the regex delimiter
   .*  - Match each entire line
     :  - and replace with
      &  - The entire line, followed by
       /8640000+719529  - this text
                      :  - End command

0a"라인 0 뒤에 텍스트 추가", 즉 버퍼(파일)의 시작 부분에 텍스트를 추가한다는 의미입니다.

text는 scale=10추가할 리터럴 텍스트입니다.

한 줄 .자체가 "추가" 명령을 종료합니다.

이 명령은 %!bc전체 버퍼의 내용을 표준 입력으로 외부 명령에 전달 bc하고 전체 버퍼를 결과 출력으로 바꿉니다.

%p전체 버퍼를 표준 출력으로 인쇄하는 방법입니다.

q!변경 사항을 저장하지 않고 종료함을 나타냅니다.


당신이 가지고 있다면아주 아주 크다파일에는 수천만 줄이 있는데, 이는 분명히 문제를 일으킬 것입니다. 나는 이 용도에 대해 가능한 솔루션을 연구했으며 ex몇 가지 방법이 있습니다.할 수 있다완료되었습니다. 그러나 저는 여전히 단지 다음을 사용하는 매우 간단한 접근 방식을 선호하여 이 접근 방식을 포기했습니다.POSIX 전용 도구.

사용split파일을 청크로 분할하고 cat결과 출력과 함께 각 청크에 대해 이전에 지정된 명령을 실행합니다.

split -l 1000000 -a 3 file.txt myprefix.
for f in myprefix.???; do
  printf '%s\n' '%s:.*:&/8640000+719529:' 0a scale=10 . '%!bc' %p 'q!' |
    ex "$f"
done > myoutputfile.txt
rm myprefix.???

split여기서 명령은 각 행이 백만 줄인 청크로 분할하는 데 사용됩니다 file.txt(물론 나머지도 파일에 저장됩니다). 지정된 대로 -a 3블록의 접미어 길이는 3자입니다. myprefix.aaa, myprefix.aab,등.

그런 다음 각 파일을 개별적으로 처리할 수 있으며 ex전체 루프의 출력을 다음으로 리디렉션하기 때문에 변경 사항을 저장할 필요가 없습니다 myoutputfile.txt(그런 다음 청크 파일을 삭제하여 깔끔하게 유지).

답변3

쉘에서 이 작업을 수행하면 매우 느립니다.

$ awk '{printf "%.10f\n", (($1/(100*86400))+719529)}' filename
735235.0000000000
735235.0000001157
735235.0000002314
735235.0000003473
735235.0000004630

마지막 항목에서 볼 수 있듯이 약간 다른 반올림 결과를 얻게 됩니다.

관련 정보