쉼표로 구분 된 파일에서만 따옴표 사이에서 쉼표를 제거하십시오.


23

쉼표 ( ,) 로 구분 된 입력 파일이 있습니다. 큰 따옴표로 묶인 필드에는 쉼표가 있습니다. 다음은 샘플 행입니다

123,"ABC, DEV 23",345,534.202,NAME

큰 따옴표와 큰 따옴표 내부에서 발생하는 모든 쉼표를 제거해야합니다. 따라서 위의 줄은 아래와 같이 파싱되어야합니다.

123,ABC DEV 23,345,534.202,NAME

나는 다음을 사용 sed했지만 예상 결과 를 얻지 못했습니다.

sed -e 's/\(".*\),\(".*\)/\1 \2/g'

어떤 빠른 트릭 sed, awk또는 다른 유닉스 유틸리티하세요?


나는 당신이 무엇을하려고하는지 확실하지 않지만 유틸리티 "csvtool"은 sed 또는 awk와 같은 일반적인 도구보다 CSV를 구문 분석하는 데 훨씬 좋습니다. 그것은 리눅스의 거의 모든 배포판에 있습니다.
figtrap

답변:


32

따옴표가 균형을 이루는 경우 다른 모든 따옴표 사이에서 쉼표를 제거하려면 다음과 awk같이 표현할 수 있습니다 .

awk -F'"' -v OFS='' '{ for (i=2; i<=NF; i+=2) gsub(",", "", $i) } 1' infile

산출:

123,ABC DEV 23,345,534.202,NAME

설명

-F"차종은 다른 모든 필드 간 인용 텍스트된다는 뜻 이중 인용 부호의 라인을 분리 awk가. for-loop는 gsub다른 모든 필드에서 쉼표 ( ",")를 아무 것도 ( "") 로 바꾸어 전 세계적으로 대체하기에 짧습니다 . 1끝에 기본 코드 블록을 호출합니다 { print $0 }.


1
gsub이 라이너가 어떻게 작동하는지 자세히 설명하고 간략 하게 설명해 주시겠습니까? 부디.
mtk

고맙습니다! 이 스크립트는 정말 잘 작동하지만 스크립트 끝에 외로운 1을 설명 할 수 있습니까? -} 1 '-
CocoaEv

@CocoaEv : 실행 { print $0 }됩니다. 나는 그것을 설명에 덧붙였다.
Thor

2
: 때때로 CSV와 같은 여러 줄에 걸쳐 행이 있습니다이 방법은 문제가있다 prefix,"something,otherthing[newline]something , else[newline]3rdline,and,things",suffix (예 : 어디서든 내에서 여러 선, 중첩을 ","여러 줄을 두 번 인용 : 전체 "...."부분은 다시 가입해야하며, 내부가 ,되어야한다 replace / removed ...) :이 경우 스크립트는 큰 따옴표 쌍을 볼 수 없으며 해결하기 쉽지 않습니다 ( "열린"에있는 줄을 "재결합"해야합니다 (예 : 홀수)). 큰 따옴표는 ... + 문자열 \" 안에 탈출이 있으면 여분의주의를 기울이십시오 )
Olivier Dulac

1
이 솔루션을 좋아했지만 종종 쉼표를 유지하고 싶지만 여전히 경계를 정하기 위해 조정했습니다. 대신 따옴표 외부 의 쉼표를 파이프로 변환하여 csv를 psv 파일로 변환했습니다.awk -F'"' -v OFS='"' '{ for (I=1; i<=NF; i+=2) gsub(",", "|", $i) } 1' infile
Danton Noriega

7

루프 와 함께 sed를 한 번만 사용 하면 좋은 응답 이 있습니다 .

echo '123,"ABC, DEV 23",345,534,"some more, comma-separated, words",202,NAME'|
  sed ':a;s/^\(\([^"]*,\?\|"[^",]*",\?\)*"[^",]*\),/\1 /;ta'
123,"ABC  DEV 23",345,534,"some more  comma-separated  words",202,NAME

설명:

  • :a; 퍼터 브랜치의 라벨입니다
  • s/^\(\([^"]*,\?\|"[^",]*",\?\)*"[^",]*\),/\1 / 3 개의 동봉 된 부품을 포함 할 수 있음
    • 첫 번째 두 번째 : [^"]*,\?\|"[^",]*",\?큰 따옴표가없는 문자열, 쉼표 또는 쉼표가없는 두 개의 큰 따옴표로 묶인 문자열과 일치합니다 .
    • 첫 번째 RE 부분 보다 이전에 설명한 부분 2보다 많은 반복이 뒤 따르고 1 개의 큰 따옴표와 일부 caracteres가 나오지만 큰 따옴표 나 코마는 없습니다.
    • 코마가 뒤 따르는 첫 번째 RE 부분.
    • Nota, 나머지 줄은 건드릴 필요가 없습니다
  • ta:a이전 s/명령이 약간 변경 되면 반복됩니다 .

중첩 된 따옴표와 함께 작동합니다. 정말 고마워요!
tricasse

5

균형 따옴표 사이에서 여러 개의 쉼표를 처리 할 수있는 일반적인 솔루션에는 중첩 된 대체가 필요합니다. 주어진 입력의 모든 줄을 처리하고 다른 모든 따옴표 쌍으로 쉼표로만 대체하는 솔루션을 perl로 구현합니다.

perl -pe 's/ "  (.+?  [^\\])  "               # find all non escaped 
                                              # quoting pairs
                                              # in a non-greedy way

           / ($ret = $1) =~ (s#,##g);         # remove all commas within quotes
             $ret                             # substitute the substitution :)
           /gex'

또는 간단히

perl -pe 's/"(.+?[^\\])"/($ret = $1) =~ (s#,##g); $ret/ge'

처리하려는 텍스트를 명령으로 파이프하거나 마지막 명령 행 인수로 처리 할 텍스트 파일을 지정할 수 있습니다.


1
(가) [^\\], 즉, 따옴표 안에 마지막 문자와 일치하며 (비 \ 문자)를 제거하는 바람직하지 않은 영향을 미칠 것입니다, 당신은 그 문자를 소비하지 않아야합니다. (?<!\\)대신 시도하십시오 .
tojrobinson

당신의 반대에 감사드립니다, 나는 그것을 고쳤습니다. 그럼에도 불구하고 나는 우리가 여기에 주장을 뒷받침 할 필요가 없다고 생각합니까?
user1146332

1
캡처 그룹에 \가 아닌 것을 포함하면 동등한 결과가 생성됩니다. +1
tojrobinson

1
+1. sed로 몇 가지 작업을 시도한 후 sed의 문서를 확인하고 일치하는 부분에 대체를 적용 할 수 없음을 확인했습니다 ... 그래서 펄을 포기하고 시도했습니다. 매우 유사한 접근 방식으로 끝났지만이 버전은 [^"]*일치하지 않는 일치를 만드는 데 사용 됩니다 (즉, 하나 "에서 다음으로 모든 것을 일치 시킵니다 ") perl -pe 's/"([^"]+)"/($match = $1) =~ (s:,::g);$match;/ge;'. 인용 부호가 백 슬래시로 이스케이프 될 수 있다는 터무니없는 생각을 인정하지 않습니다. :-)
cas

귀하의 의견에 감사드립니다. [^"]*접근 방식이나 욕심없는 접근 방식이 CPU 시간을 덜 소비 한다면 흥미로울 것 입니다.
user1146332

3

적절한 CSV 파서가있는 언어를 사용합니다. 예를 들면 다음과 같습니다.

ruby -r csv -ne '
  CSV.parse($_) do |row|
    newrow = CSV::Row.new [], []
    row.each {|field| newrow << field.delete(",")}
    puts newrow.to_csv
  end
' < input_file

처음에이 솔루션을 좋아했지만 큰 파일의 경우 속도가 매우 느리다는 것이 밝혀졌습니다.
KIC

3

두 번째 인용문이 잘못 배치되었습니다.

sed -e 's/\(".*\),\(.*"\)/\1 \2/g'

또한 정규식을 사용하면 텍스트의 가장 긴 부분과 일치하는 경향이 있습니다. 즉, 문자열에 인용 필드가 둘 이상 있으면 작동하지 않습니다.

sed에서 여러 인용 필드를 처리하는 방법

sed -e 's/\(\"[^",]\+\),\([^",]*\)/\1 \2/g' -e 's/\"//g'

그러나 이것은 인용 된 필드 당 쉼표를 두 개 이상 포함 할 수있는 입력을 사용하여 sed의 첫 번째 표현식이 단일 필드의 최대 쉼표 내용 수만큼 또는 반복 될 때까지 반복되어야합니다 출력을 전혀 변경하지 않습니다.

둘 이상의 표현식으로 sed를 실행하면 여러 개의 sed 프로세스가 실행되고 "tr"이 모두 열린 파이프로 실행되는 것보다 효율적이어야합니다.

그러나 입력 형식이 올바르지 않으면 바람직하지 않은 결과가 발생할 수 있습니다. 즉, 중첩 된 따옴표, 종료되지 않은 따옴표입니다.

실행중인 예제 사용 :

echo '123,"ABC, DEV 23",345,534,"some more, comma-separated, words",202,NAME' \
| sed -e 's/\(\"[^",]\+\),\([^",]*\)/\1 \2/g' \
-e 's/\(\"[^",]\+\),\([^",]*\)/\1 \2/g' -e 's/\"//g'

산출:

123,ABC  DEV 23,345,534,some more  comma-separated  words,202,NAME

조건부 분기를 사용하여 좀 더 일반적으로 만들 수 있고, 예를 들어 GNU sed : 등의 ERE를 사용하면 더 읽기 쉽습니다 sed -r ':r; s/("[^",]+),([^",]*)/\1 \2/g; tr; s/"//g'.
Thor

2

펄에서-당신은 Text::CSV이것을 파싱하고 사소하게 할 수 있습니다 :

#!/usr/bin/env perl
use strict;
use warnings;

use Text::CSV; 

my $csv = Text::CSV -> new();

while ( my $row = $csv -> getline ( \*STDIN ) ) {
    #remove commas in each field in the row
    $_ =~ s/,//g for @$row;
    #print it - use print and join, rather than csv output because quotes. 
    print join ( ",", @$row ),"\n";
}

인쇄 할 수는 Text::CSV있지만 따옴표를 유지하는 경향이 있습니다. ( 단, 출력에 따옴표를 제거Text::CSV 하는 대신 처음부터 사용하여 구문 분석 할 수 있습니다 .)


0

문자열의 모든 문자를 통해 루프하는 함수를 만들었습니다.
문자가 인용문이면 검사 (b_in_qt)가 true로 표시됩니다.
b_in_qt가 true 인 동안 모든 쉼표는 공백으로 바뀝니다.
다음 쉼표를 찾으면 b_in_qt가 false로 설정됩니다.

FUNCTION f_replace_c (str_in  VARCHAR2) RETURN VARCHAR2 IS
str_out     varchar2(1000)  := null;
str_chr     varchar2(1)     := null;
b_in_qt     boolean         := false;

BEGIN
    FOR x IN 1..length(str_in) LOOP
      str_chr := substr(str_in,x,1);
      IF str_chr = '"' THEN
        if b_in_qt then
            b_in_qt := false;
        else
            b_in_qt := true;
        end if;
      END IF;
      IF b_in_qt THEN
        if str_chr = ',' then
            str_chr := ' ';
        end if;
      END IF;
    str_out := str_out || str_chr;
    END LOOP;
RETURN str_out;
END;

str_in := f_replace_c ("blue","cat,dog,horse","",yellow,"green")

RESULTS
  "blue","cat dog horse","",yellow,"green"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.