Unix: znajdź i zamień kolejne przecinki na kolejne potoki

Jan 08 2021

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

2 WiktorStribiżew Jan 08 2021 at 22:37

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 kontekstach
  • defined($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- goznacza global(zastępuje wszystkie wystąpienia) i esprawia, ż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
4 potong Jan 08 2021 at 23:05

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.

1 RamanSailopal Jan 09 2021 at 00:35

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
1 Daweo Jan 09 2021 at 01:50

Użyłbym GNU AWKdo tego w następujący sposób. Niech file.txtbę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)