행을 지정된 수의 필드로 줄 바꿈

행을 지정된 수의 필드로 줄 바꿈

아래와 같이 여러 벡터가 포함된 텍스트 파일이 있습니다. 이러한 벡터의 구성요소는 공백으로 구분되며 여러 줄에 걸쳐 분산됩니다. 이 파일은 Ubuntu 터미널에서 명령을 실행한 후에 생성되었습니다.

0 -1 -0.494 0.12 -0.919 0.112 0.914 -0.681 -0.067 -0.918 -0.443 -0.216 -0.48 0.55 0.701 0.429 0.699 -0.726 -0.39 0.172 0.61 -0.599 0.728 -0.883 -0.32 0.044 -0.189 -0.732 -0.309 -0.286 -0.859 0.107 0.298 
0 0 0.869 0.641 -0.331 -0.631 -0.236 0.303 0.998 0.153 -0.89 -0.927 -0.671 -0.478 0.693 -0.007 -0.64 0.091 -0.249 -0.881 0.641 0.689 0.222 -0.398 0.548 -0.268 -0.877 -0.333 -0.55 0.858 0.504 0.215 -0.178 
0 0 0 0.758 -0.214 0.768 0.329 0.667 -0.013 0.367 0.103 -0.307 -0.565 0.685 0.171 -0.903 0.32 -0.682 -0.887 -0.44 -0.467 0.409 -0.649 0.249 0.772 -0.962 0.443 -0.594 0.776 -0.427 0.088 -0.971 0.938 

다음 형식의 셸 명령을 사용하여 이 파일을 다른 파일로 변환하려면 어떻게 해야 합니까? 여기서 각 벡터는 별도의 줄에 있고 파일 헤더는 3성분 벡터의 수입니다.

n
V1x V1y V1z
V2x V2y V2z
V3x V3y V3z
...
Vnx Vny Vnz

여기서 n은 파일에 있는 3성분 벡터의 수입니다. 내 파일 : V1x=0, V1y=-1, V1z=-0.494, 등 V2x=0.12.V2y=-0.919V2z=0.112

답변1

Perl 코드 한 줄:

perl -p00E 'y/\n/ /;say s/(\S+\s+){3}\K/\n/g' file

이 솔루션은 다음과 같습니다.그누크스파일이 전체적으로 메모리에 저장될 만큼 작다고 가정합니다.

설명하다

  • -p파일의 각 레코드에 별칭이 지정되고 각 레코드를 처리한 후 내용이 인쇄됨을 나타냅니다 $_.$_
  • -00전체 파일을 하나의 레코드로 읽을 수 있도록 레코드 구분자를 비워 두라는 것을 나타냅니다.
  • -E다음 문자열이 Perl 코드로 처리됨을 나타냅니다. -E일반적인 방법 대신 사용하면 해당 기능을 -e사용할 수 있습니다 say.
  • y/\n/ /전체 파일을 한 줄로 만듭니다( 사용자 편의를 위해 y///Perl에서는 동의어입니다 ).tr///sed
  • s/(\S+\s+){3}\K/\n/g각 패턴 뒤에 새 줄을 추가하는 것을 의미합니다(공백이 아닌 공백이 3번 반복됨 == 벡터).
  • s///성공적으로 치환된 횟수가 반환되므로, 이를 인자로 사용하면 치환 say횟수(=벡터)가 출력됩니다.
  • 카운트를 인쇄한 후 $_인쇄되는 내용은 우리가 사용한 것과 같습니다 -p.

고쳐 쓰다

최대값을 원할 경우:

perl -p00E 'y/\n/ /;s/(\S+\s+){1}\K/\n/g' file | sort -nr | head -1

이 솔루션의 장점

"매직 넘버"는 단 하나뿐입니다. 즉, 갑자기 2D 벡터를 사용하기 시작했다면 {3}코드를 {2}.

이 솔루션의 단점

Perl에 익숙하지 않다면 이는 흑마술 주문처럼 읽힐 수 있습니다.

답변2

그것은 마치

ruby -e 'ns = STDIN.read.split(/\s+/); puts(ns.size/3); 0.step(ns.size,3) do |i| puts(ns[i,3].join(" ")) end' < yourfile

쉘에서 외부 프로그램 호출을 허용하면 작동합니다.

편집: 아마도 골프 코스에서 이것을 해야 할 것 같습니다 :-)

답변3

따라서 두 가지 작업을 수행하고 싶습니다.

  • 각 행이 정확히 3개의 좌표를 갖도록 데이터를 다시 패키지합니다.
  • 한 줄의 벡터 수에 접두사를 붙입니다.

이를 두 개의 연속적이고 독립적인 문제로 처리하는 것이 더 간단할 것입니다. 먼저 데이터를 다시 패키지합니다. 이를 위해 awk를 사용하여 공백 시퀀스가 ​​입력 레코드 구분 기호임을 알릴 수 있습니다.

awk -v RS='[[:space:]]+' '{if (NR % 3) printf "%s ", $0; else print}' <input.txt >wrapped.txt

출력 구분 기호를 줄 번호 3개(또는 공백)의 배수인 개행 문자로 설정하여 이 길이를 줄일 수 있습니다.

awk -v RS='[[:space:]]+' '{ORS = NR % 3 ? " " : "\n"; print}' <input.txt >wrapped.txt

인쇄가 기본 작업이므로 다음과 같이 단축할 수 있습니다.

awk -v RS='[[:space:]]+' 'ORS = NR % 3 ? " " : "\n"' <input.txt >wrapped.txt

벡터 수는 중간 파일의 라인 수입니다.

wc -l wrapped.txt >output.txt
cat wrapped.txt >>output.txt

답변4

printfBash 쉘에는 흥미로운 내장 기능이 있습니다.

  The format is reused as necessary to consume all  of  the  argu‐
  ments.

이를 통해 공백으로 구분된 값 파일을 읽고 간단한 printf를 사용하여 이를 3줄로 뱉어낼 수 있는 것 같습니다.

printf '%8.3f %8.3f %8.3f\n' $(<file)

(저는 단지 출력을 아름답게 하기 위해 부동 소수점 형식을 사용하고 있지만 각 필드를 원시 문자열로 에코하는 8.3데 사용할 수 있습니다 .)%s

결과 벡터를 계산하려면 사용할 수 있습니다 wc. 계산해도 괜찮다면뒤쪽에데이터를 입력한 다음 출력할 수 있습니다.

printf '%8.3f %8.3f %8.3f\n' $(<file) | tee >(wc -l)

개수를 맨 위에 두도록 고집하는 경우 한 가지 가능성은 변수에 인쇄한 다음 변수를 계산하고 인쇄하는 것입니다(이는 다른 내부 메서드와 동일한 메모리 고려 사항이 적용됩니다).

printf -v vecs '%8.3f %8.3f %8.3f\n' $(<file)
wc -l < <(printf "$vecs") ; printf "$vecs"

진정한 쉘 순수주의자라면 mapfile(또는 동의어 )를 사용하여 readarray문자열 변수 대신 형식이 변경된 데이터를 배열에 넣은 다음 쉘의 ${#array[@]}개수 연산자를 사용하여 외부 호출을 피할 수 있습니다.wc

mapfile vecs < <(printf '%8.3f %8.3f %8.3f\n' $(<file))
printf '%d\n' ${#vecs[@]} ; printf '%s' "${vecs[@]}"

최종 printf는 다시 형식 재사용을 사용하여 개행으로 끝나는 각 배열 요소를 순서대로 인쇄합니다.

관련 정보