CSV 필드에서 포함된 줄 바꿈을 제거하는 방법

CSV 필드에서 포함된 줄 바꿈을 제거하는 방법

파일 때문에 고민 중이에요. 일반적으로 호스트당 한 줄을 정의해야 합니다. 그러나 때때로 누군가가 일부 필드를 다른 행으로 분할하는 경우가 있습니다. 예는 다음과 같습니다.

"host1","host1","linux
server",""
"host2","host2","linux server",""

이제 나는 이 문제를 해결할 수 있는 방법(bash에서 더 나은 방법)을 찾고 싶습니다.

"host1","host1","linux server",""
"host2","host2","linux server",""

각 필드는 큰따옴표로 묶어야 합니다. 그렇지 않은 경우 \na가 삽입된 다음 이를 제거하여 행당 항상 4개의 필드가 있음을 의미합니다.

설명을 여러 줄로 나눌 수 있습니다. 예를 들면 다음과 같습니다.

"host1","host1","linux
server
centos",""
"host2","host2","linux server",""

awk나는 다음과 같은 여러 가지 접근법을 시도했습니다 .

awk 'BEGIN {ORS=""; RS="\"\n\""; FS="\",\""; OFS="\",\""} {if (NF == 3) print "\"" $1 "\"," $2 "\"," $3 "\"\n"; else printf "%s", $0} END {print ""}' /tmp/ngr4

그러나 나는 성공하지 못했고 이 강력한 도구로 인해 한계에 도달하기 시작했습니다.

답변1

사용밀러( mlr), 다양한 구조화된 문서 형식에 대한 CSV 지원 기능을 갖춘 다목적 처리 유틸리티로 모든 필드에서 공백을 정리하는 데 사용됩니다.

$ cat file
"host1","host1","linux
server",""
"host2","host2","linux server",""
$ mlr --csv -N clean-whitespace file
host1,host1,linux server,
host2,host2,linux server,

데이터를 file헤더 없는 CSV 레코드로 읽고 적용합니다.clean-whitespace작업모든. 이 clean-whitespace작업은 각 필드 값에서 측면 공백을 잘라내고 연속 공백 문자를 단일 공백으로 결합합니다.

로 변경줄 바꿈을 공백으로만 바꾸세요, 짧은 문을 사용하여 필드를 반복할 수 있습니다.put표현하다:

$ mlr --csv -N put 'for (k,v in $*) { $[k] = gssub(v, "\n", " ") }' file
host1,host1,linux server,
host2,host2,linux server,

gssub()기능이는 Awk처럼 동작 gsub()하지만 쿼리 매개변수를 정규식으로 처리하지 않습니다(Miller도 마찬가지입니다 gsub()).

꼭 필요한 것은 아니더라도 필드에 인용을 붙여야 한다고 생각되면(필드 값에 필요한 경우 Miller가 자동으로 인용을 추가합니다) 옵션 mlr과 함께 사용하세요 --quote-all.

$ mlr --csv -N --quote-all clean-whitespace file
"host1","host1","linux server",""
"host2","host2","linux server",""
$ mlr --csv -N --quote-all put 'for (k,v in $*) { $[k] = gssub(v, "\n", " ") }' file
"host1","host1","linux server",""
"host2","host2","linux server",""

답변2

마지막으로 하고 싶은 일은 bash에서 이 작업을 수행하는 것입니다. 바라보다쉘 루프를 사용하여 텍스트를 처리하는 것이 왜 나쁜 습관으로 간주됩니까?.

이제 원하는 것을 "문자 바로 뒤에 나오지 않는 한 줄 바꿈을 제거하십시오 ""라고 표현할 수 있다면 다음을 수행할 수 있습니다.

perl -pe 's/(?<!")\n/ /g' file

(?<!")\n앞에 없는 개행 문자와 일치합니다 ". 따라서 다음과 같은 입력 예가 주어집니다.

$ cat file
"host0","host0","linux
server",""
"host1","host1","linux
server
centos",""
"host2","host2","linux server",""

위 명령은 다음을 제공합니다.

$ perl -pe 's/(?<!")\n/ /g' file
"host0","host0","linux server",""
"host1","host1","linux server centos",""
"host2","host2","linux server",""

그런데 사실 mlr이게 가장 좋은 방법이에요.

답변3

사용행복하다(이전 Perl_6)

@terdon의 뛰어난 Perl 답변에서 영감을 얻었습니다.

~$ raku -ne '/ <!after \" > $/ ?? print "$_ " !! put $_;'  file

아래는 Raku(일명 Perl6)로 작성된 답변입니다. Raku에는 잘 알려진 일부 관용어를 정리하려고 시도하는 새로운 유니코드 인식 정규식 엔진이 있습니다. 따라서 (예를 들어) "Y not after X" 부정적인 되돌아보기 관용어는 <!after X > YRaku에서 <?after … >긍정적인 되돌아보기를 의미하고 <!after … >부정적인 되돌아보기를 의미합니다.

\nRaku는 줄 종결자 처리를 표준화하므로(이제 줄 바꿈은 기본적으로 자동으로 잘립니다) Raku의 삼항 연산자를 사용하여 패턴을 감지할 수 있습니다.시험 ?? 진짜 !! 잘못된, 그런 다음 print( \n개행을 추가하지 않고) 또는 put( \n텍스트 끝에 개행을 추가)을 사용하여 출력합니다.


입력 예:

"host0","host0","linux
server",""
"host1","host1","linux
server
centos",""
"host2","host2","linux server",""

예제 출력:

"host0","host0","linux server",""
"host1","host1","linux server centos",""
"host2","host2","linux server",""


기타 Raku 솔루션

Raku Text::CSV모듈 사용:

Raku 생태계의 적절한 CSV 파서(모듈)를 사용하는 것은 매우 쉽습니다(참조:https://raku.land/?q=CSV). 이는 RFC 4180 준수 여부를 확인하고 표준화된 CSV 출력은 물론 광범위한 사용자 정의를 제공할 수 있습니다.

아래에서 Raku의 Text::CSV모듈은 OP의 입력을 훌륭하게 구문 분석하고 \n개행을 제거한 후 기본적으로 내부 공백이 있는 열만 큰따옴표로 묶인 열(첫 번째 답변)로 출력합니다. 두 번째 답변은 한 줄씩 읽고 첫 번째 답변과 동일한 답변을 생성합니다.

전체 파일을 메모리로 읽어옵니다 csv(). 기본 출력은 다음과 같습니다.

~$ raku -MText::CSV -e 'my @a = csv(in => "/path/to/file", sep => ",");
                        @a = @a>>.map( *.trans: "\n" => " ");
                        csv(in => @a, out => $*OUT, sep => ",");'
host0,host0,"linux server",
host1,host1,"linux server centos",
host2,host2,"linux server",

한 줄씩 읽은 다음 출력을 "수동으로" 인용합니다.

~$ raku -MText::CSV -e 'my $fh = "/path/to/file";  my $io = open $fh, :r, :!chomp; 
                        my $csv = Text::CSV.new;  my @data;
                        while $csv.getline($io) -> $row {
                            @data.push: $row.map: *.trans: "\n" => " "; };
                        put $_.join(",") for @data>>.map({ / \s / ?? (q["] ~ $_ ~ q["]) !! $_ });'
host0,host0,"linux server",
host1,host1,"linux server centos",
host2,host2,"linux server",

https://docs.raku.org/언어/operators#infix_??_!!
https://github.com/Tux/CSV/blob/master/doc/Text-CSV.md#embedded-newlines
https://raku.org

답변4

원치 않는 개행 문자가 많더라도 큰따옴표는 모두 일치하고 필드 구분 기호는 모두 존재한다고 가정합니다. 이 경우 다음 명령을 사용할 수 있습니다.

$ sed '/^"/! s/^/ /'  infile | tr -d '\n' | sed '-e s/"/"\n/'{8..1000..8}
"host1","host1","linux server centos",""
"host2","host2","linux server",""

어디:

$ cat infile
"host1","host1","linux
server
centos",""
"host2","host2","linux server",""

이는 원치 않는 개행이 발생할 수 있는 모든 곳에서 작동합니다.

중괄호 안의 숫자 1000은 임의의 큰 숫자이며 입력 파일의 총 문자 수보다 커야 합니다.

입력 줄에 선행/후행 공백이 포함되어 있다고 의심되면 먼저 공백을 제거하십시오. 예를 들어 다음 명령을 사용하십시오.awk 'NF{$1=$1}1' infile

관련 정보