Unix: znajdź i zamień kolejne przecinki na kolejne potoki
Konwertuję plik CSV z podwójnym cudzysłowem na plik txt rozdzielany potokami w systemie Unix. Użyłem następującego polecenia sed, aby zamienić „,” na | następnie usuń początkowy i końcowy podwójny cudzysłów.
sed -e 's/","/|/g' -e 's/"//g' filenm.csv > filenm.txt
Ale wydaje się, że plik zawiera kolejne przecinki bez podwójnych cudzysłowów i nie są one zastępowane.
Col1|col2|col3|col4|col5|col6|col7|col8
Val1|val2|val3,,,,val7|val8
Teraz chcę przekonwertować wszystkie te kolejne przecinki na kolejne potoki, ponieważ wskazują one puste lub puste pola.
Inne pola również mają przecinki w wartościach pól, których nie należy zmieniać.
Próbowałem użyć poniżej, ale nie działało.
sed -e 's/,{1,\}/|{1,\}/g' filenm.csv > filenm.txt
przykładowy plik csv otwarty w notatniku:
"ID","Name","DOB","Age","Address","City","State","Country","Phone number"
"123","ABC","12/20/2020","15","No.38,3rd st, RRR NNN, TRT",,,,"9999999999"
"456","DEF","12/20/2020",,,,,"test-country","9999999999"
"465","XYZ",,,"No.38,3rd st, RRR NNN, TRT",,,,"9999999999"
Mam nadzieję, że pomoże to odtworzyć i rozwiązać problem.
Z góry dziękuję....
Odpowiedzi
Możesz użyć perl
:
perl -pe 's/"([^"]*)"|,/defined($1) ? $1 : "|"/ge' filenm.csv > filenm.txt
Szczegóły :
"([^"]*)"|,
- wzorzec wyrażenia regularnego, który pasuje"
, a następnie przechwytuje do Grupy 1 dowolne zero lub więcej znaków innych niż,"
a następnie dopasowuje a"
lub po prostu dopasowuje a,
we wszystkich innych kontekstachdefined($1) ? $1 : "|"
- RHS, zamiana, która zastępuje dopasowanie wartością grupy 1 (jeśli dopasowano grupę 1) lub|
(jeśli,
została dopasowana)ge
-g
oznaczaglobal
(zastępuje wszystkie wystąpienia) ie
sprawia, że Perl traktuje RHS jako wyrażenie Perla.
Zobacz test online :
#!/bin/bash
s='"ID","Name","DOB","Age","Address","City","State","Country","Phone number"
"123","ABC","12/20/2020","0","No.38,3rd st, RRR NNN, TRT",,,,"9999999999"'
perl -pe 's/"([^"]*)"|,/defined($1) ? $1 : "|"/ge' <<< "$s"
Wynik:
ID|Name|DOB|Age|Address|City|State|Country|Phone number
123|ABC|12/20/2020|0|No.38,3rd st, RRR NNN, TRT||||9999999999
To może zadziałać dla Ciebie (GNU sed):
sed -E ':a;s/^(("[^",]*",+)*"[^",]*),/\1\n/;ta;y/,\n/|,/' file
Iteracyjnie zamień ,
's między "
' s na nowe linie, a następnie przetłumacz ,
's dla |
' s i nowe linie dla ,
's.
Korzystanie z awk:
awk -F \" '{ for(i=1;i<=NF;i++) { if ($i ~ /^[,]{2,}$/) { $i="," } } OFS="\"";gsub("\",\"","\"|\"",$0)}1' sample.csv
Wyjaśnienie:
awk -F \" '{ # Set the field delimiter to double quote
for(i=1;i<=NF;i++) {
if ($i ~ /^[,]{2,}$/) {
$i="," # Loop through each field and if is contains 2 or more commas, set that field to one comma } } OFS="\""; gsub("\",\"","\"|\"",$0) # Substitute "," for "|"
}1' sample.csv
Użyłbym GNU AWK
do tego w następujący sposób. Niech file.txt
będzie zadowolona
"ID","Name","DOB","Age","Address","City","State","Country","Phone number"
"123","ABC","12/20/2020","15","No.38,3rd st, RRR NNN, TRT",,,,"9999999999"
"456","DEF","12/20/2020",,,,,"test-country","9999999999"
"465","XYZ",,,"No.38,3rd st, RRR NNN, TRT",,,,"9999999999"
następnie
awk 'BEGIN{FS="\"";OFS=""}{for(i=1;i<=NF;i+=2){$i=gensub(/,/,"|","g",$i)};print $0}' file.txt
wynik
ID|Name|DOB|Age|Address|City|State|Country|Phone number
123|ABC|12/20/2020|15|No.38,3rd st, RRR NNN, TRT||||9999999999
456|DEF|12/20/2020|||||test-country|9999999999
465|XYZ|||No.38,3rd st, RRR NNN, TRT||||9999999999
Założyłem, że pierwsza i ostatnia kolumna nigdy nie jest pusta. Używam "
jako separatora pól, a następnie w każdym nieparzystym polu (zawierają one wyłącznie ,
) zmieniam wszystko ,
na |
. Wreszcie drukuję całą tak zmienioną linię.
(przetestowano w GNU Awk 5.0.1)