Mam plik wejściowy rozdzielany przecinkami ( ,
). Niektóre pola są ujęte w podwójny cudzysłów i zawierają przecinek. Oto przykładowy wiersz
123,"ABC, DEV 23",345,534.202,NAME
Muszę również usunąć wszystkie przecinki występujące wewnątrz podwójnych cudzysłowów i podwójnych cudzysłowów. Tak więc powyższa linia powinna zostać przeanalizowana, jak pokazano poniżej
123,ABC DEV 23,345,534.202,NAME
Próbowałem następujących przy użyciu, sed
ale nie dając oczekiwanych rezultatów.
sed -e 's/\(".*\),\(".*\)/\1 \2/g'
Wszelkie szybkich sztuczki z sed
, awk
lub jakiekolwiek inne narzędzie UNIX proszę?
text-processing
sed
awk
csv
Mt.
źródło
źródło
Odpowiedzi:
Jeśli cytaty są zrównoważone, będziesz chciał usunąć przecinki między każdym innym cytatem, można to wyrazić w
awk
następujący sposób:Wydajność:
Wyjaśnienie
Te
-F"
marki awk oddzielić linię na oznak dwukrotnie środki, co oznacza, że każda inna dziedzina będzie tekst między cytat. Przebiegi for-loopgsub
, skrót od globalnie zastępują, na każdym innym polu, zastępując przecinek (","
) nic (""
).1
Na koniec wywołuje blok kodu:{ print $0 }
.źródło
gsub
i wyjaśnić w skrócie, jak działa ten jeden liner? Proszę.{ print $0 }
. Dodałem to również do wyjaśnienia.prefix,"something,otherthing[newline]something , else[newline]3rdline,and,things",suffix
(tj.: kilka linii i zagnieżdżone „,” w dowolnym miejscu w podwójnym cudzysłowiu wielu linii: cała"...."
część powinna być ponownie połączona, a wewnątrz,
powinna być zastąpiony / usunięty ...): twój skrypt nie zobaczy w tym przypadku par podwójnych cudzysłowów i nie jest to tak naprawdę łatwe do rozwiązania (trzeba „ponownie dołączyć” wiersze, które są w „otwartych” (tzn. nieparzystych) podwójny cytat ... + zachowaj szczególną ostrożność, jeśli\"
wewnątrz łańcucha ucieknie również ucieczka )awk -F'"' -v OFS='"' '{ for (I=1; i<=NF; i+=2) gsub(",", "|", $i) } 1' infile
Jest dobra reakcja, używając sed po prostu raz z pętlą :
Wyjaśnienie:
:a;
to etykieta dla branży rolniczejs/^\(\([^"]*,\?\|"[^",]*",\?\)*"[^",]*\),/\1 /
może zawierać 3 zamknięte części[^"]*,\?\|"[^",]*",\?
dopasuj ciąg bez podwójnego cudzysłowu, może po nim śpiączka lub ciąg zamknięty dwoma podwójnymi cudzysłowami, bez śpiączki i być może po śpiączce.ta
zapętli się,:a
jeśli poprzednies/
polecenie coś zmieniło.źródło
Ogólne rozwiązanie, które może również obsługiwać kilka przecinków między zrównoważonymi cudzysłowami, wymaga zagnieżdżonego podstawienia. Implementuję rozwiązanie w perlu, które przetwarza każdy wiersz danego wejścia i zastępuje przecinki tylko w każdej innej parze cudzysłowów:
lub w skrócie
Możesz potokować tekst, który chcesz przetworzyć, do polecenia lub określić plik tekstowy do przetworzenia jako argument ostatniego wiersza polecenia.
źródło
[^\\]
Będzie mieć niepożądany efekt dopasowania ostatni znak wewnątrz cudzysłowów i usunięcie go (non \ znaków), to znaczy, że nie należy spożywać ten znak. Spróbuj(?<!\\)
zamiast tego.[^"]*
do sprawiają, że mecz nie chciwy (tj pasuje wszystko od jednego"
do następnego"
)perl -pe 's/"([^"]+)"/($match = $1) =~ (s:,::g);$match;/ge;'
. To nie potwierdza dziwacznego pomysłu, że cytat można uniknąć odwrotnego ukośnika :-)[^"]*
podejście, albo jawne niechciwe podejście, zużywa mniej czasu procesora.Użyłbym języka z odpowiednim parserem CSV. Na przykład:
źródło
Twoje drugie cytaty są niewłaściwe:
Ponadto używanie wyrażeń regularnych zwykle pasuje do najdłuższej możliwej części tekstu, co oznacza, że to nie zadziała, jeśli w łańcuchu będzie więcej niż jedno pole cytowane.
Sposób, który obsługuje wiele cytowanych pól w sed
Jest to również sposób na rozwiązanie tego problemu, jednak w przypadku danych wejściowych, które mogą zawierać więcej niż jeden przecinek na cytowane pole, pierwsze wyrażenie w sed musiałoby być powtarzane tyle razy, ile maksymalna zawartość przecinka w jednym polu, lub dopóki w ogóle nie zmienia wyjścia.
Uruchamianie sed z więcej niż jednym wyrażeniem powinno być bardziej wydajne niż kilka uruchomionych procesów sed i „tr” wszystkie z otwartymi potokami.
Może to jednak mieć niepożądane konsekwencje, jeśli dane wejściowe nie zostaną poprawnie sformatowane. tzn. cytaty zagnieżdżone, cytaty niezakończone.
Korzystając z działającego przykładu:
Wydajność:
źródło
sed -r ':r; s/("[^",]+),([^",]*)/\1 \2/g; tr; s/"//g'
.W perlu - możesz go użyć
Text::CSV
do parsowania tego i rób to trywialnie:Możesz drukować za pomocą,
Text::CSV
ale zazwyczaj zachowuje cytaty. (Chociaż sugerowałbym - zamiast usuwania cytatów z wyników, możesz po prostu parsować używającText::CSV
w pierwszej kolejności).źródło
Stworzyłem funkcję umożliwiającą zapętlanie każdego znaku w ciągu.
Jeśli znak jest cytatem, wówczas czek (b_in_qt) jest oznaczony jako prawda.
Podczas gdy b_in_qt jest prawdziwe, wszystkie przecinki są zastępowane spacją.
b_in_qt jest ustawione na false, gdy zostanie znaleziony następny przecinek.
źródło