Usuń tylko przecinki obecne w podwójnych cudzysłowach

10

W pliku tekstowym chcę usunąć ,(przecinki), a także "(cudzysłowy) (tylko jeśli podwójne cudzysłowy zawierają liczby oddzielone przecinkami).

56,72,"12,34,54",x,y,"foo,a,b,bar"

Oczekiwany wynik

56,72,123454,x,y,"foo,a,b,bar"

Uwaga: powyższą linię pokazuję tylko jako przykład. Mój plik tekstowy zawiera wiele wierszy, jak wyżej, a liczby oddzielone przecinkami w podwójnych cudzysłowach powinny się różnić. To jest,

56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"

Oczekiwany wynik:

56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

W npodwójnych cudzysłowach jest wiele liczb oddzielonych przecinkami. A także pozostaw podwójne cudzysłowy, które zawierają znaki w niezmienionej postaci.

Uwielbiam sednarzędzie do przetwarzania tekstu. Cieszę się, jeśli opublikujesz jakieś sedrozwiązanie tego problemu.

Avinash Raj
źródło
Od 56,72,"12,34,54",x,y,"foo,a,b,bar"do 56,72,123454,x,y,"a,b", fooa barznaczy zniknąć. Czy to twoja pożądana wydajność?
cuonglm
Użyty przykład jest nieco mylący, ponieważ niektóre elementy (jak fooi bar) są usuwane wraz z przecinkami. Ponadto niektóre cytaty znikają tam, gdzie pozostają inne. Nie wspominając, że przecinki między ai bpozostają również. Czy jest w tym jakiś wzór?
HalosGhost
edytowałem przepraszających przyjaciół.
Avinash Raj
Twoje zmiany nie wyjaśniły Twojego przykładu. Proszę zobaczyć mój ostatni komentarz .
HalosGhost
usuń wszystkie przecinki z podwójnych cudzysłowów, a także cudzysłowy tylko wtedy, gdy cudzysłowy zawierają liczby.
Avinash Raj

Odpowiedzi:

7

To (zaadaptowane stąd ) powinno zrobić to, czego potrzebujesz, chociaż Perl @ rici jest o wiele prostszy:

$ sed -r ':a;s/(("[0-9,]*",?)*"[0-9,]*),/\1/;ta; s/""/","/g; 
          s/"([0-9]*)",?/\1,/g ' file
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454,
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

Wyjaśnienie

  • :a: zdefiniuj etykietę o nazwie a.
  • s/(("[0-9,]*",?)*"[0-9,]*),/\1/ : Ten trzeba rozbić
    • Przede wszystkim, za pomocą tego konstruktu: (foo(bar)), \1będzie foobari \2będzie bar.
    • "[0-9,]*",?: dopasuj 0 lub więcej z 0-9lub ,, a następnie 0 lub 1 ,.
    • ("[0-9,]*",?)* : dopasuj 0 lub więcej z powyższych.
    • "[0-9,]*: dopasuj 0 lub więcej 0-9lub ,które pojawiają się zaraz po"
  • ta;: wróć do etykiety ai uruchom ponownie, jeśli podstawienie się powiodło.
  • s/""/","/g;: przetwarzanie końcowe. Wymień ""się ",".
  • s/"([0-9]*)",?/\1,/g : usuń wszystkie cudzysłowy wokół liczb.

Można to łatwiej zrozumieć na innym przykładzie:

$ echo '"1,2,3,4"' | sed -nr ':a;s/(("[0-9,]*",?)*"[0-9,]*),/\1/;p;ta;'
"1,2,34"
"1,234"
"1234"
"1234"

Tak więc, podczas gdy możesz znaleźć liczbę, która jest tuż po cytacie, po której następuje przecinek i inny numer, połącz dwie liczby razem i powtarzaj proces, aż nie będzie to już możliwe.

W tym momencie uważam, że warto wspomnieć o cytacie z info sedtego, który pojawia się w sekcji opisującej zaawansowane funkcje, takie jak etykieta użyta powyżej (dzięki za znalezienie if @Braiam):

W większości przypadków użycie tych poleceń wskazuje, że prawdopodobnie lepiej jest programować w coś takiego jak `awk 'lub Perl.

terdon
źródło
10

Jeśli perl jest w porządku, oto krótki (i prawdopodobnie szybki, jeśli niekoniecznie prosty :)) sposób:

perl -pe 's:"(\d[\d,]+)":$1=~y/,//dr:eg' file

eFlag do s:::operatora (co jest tylko kolejnym sposobem na piśmie s///) powoduje zastąpienie być traktowane jako wyraz, który jest oceniany za każdym razem. Wyrażenie to pobiera wyrażenie $1z wyrażenia regularnego (w którym już brakuje cudzysłowów) i tłumaczy ( y///które można również zapisać jako tr///), usuwając ( /d) wszystkie przecinki. rFlagę yjest konieczne w celu uzyskania wartości będzie przetłumaczony łańcuch zamiast liczby tłumaczeń.

Dla tych, którzy w jakiś sposób czują się przygnębieni przez perla, oto odpowiednik python. Python tak naprawdę nie jest narzędziem powłoki jednowierszowej, ale czasami można go przekonać do współpracy. Poniższe można zapisać jako jedną linię (w przeciwieństwie do forpętli, które nie mogą być), ale przewijanie w poziomie sprawia, że ​​(jeszcze bardziej) jest nieczytelny:

python -c '
import re;
import sys;
r=re.compile("\"(\d+(,\d+)*)\"");
all(not sys.stdout.write(r.sub(lambda m:m.group(1).replace(",",""),l))
    for l in sys.stdin)
' < file
rici
źródło
@rici: Dobry! I użyj y///zamiast tr///uratować nam jeszcze jedną postać.
cuonglm
6

W przypadku danych CSV użyłbym języka z prawdziwym analizatorem składni CSV. Na przykład z Ruby:

ruby -rcsv -pe '
  row = CSV::parse_line($_).map {|e| e.delete!(",") if e =~ /^[\d,]+$/; e} 
  $_  = CSV::generate_line(row)
' <<END
56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"
END
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"
Glenn Jackman
źródło
0

Zablokować cytat

Cześć Oto kod Pythona , który zastępuje przecinki podwójnymi cudzysłowami, przecinki są zastępowane znakiem potoku (|)

Ten kod Pythona ma zastępować przecinki ujęte w podwójne cudzysłowy

np .: x, y, z, 1,2, „r, e, t, y”, h, 8,5,6

jeśli zastąpić Rura x, y, z, 1,2, „r | e | t | y”, h, 8,5,6

jeśli zamień na null x, y, z, 1,2, „rety”, h, 8,5,6

writingFile = open('FileToWrite', 'w')
with open('FileToRead') as f:

    while True:

        c = f.read(1)
        if not c:
            print ("End of file")
            break
        print ("Read a character:", c)


        if c=='"':
            writingFile.write(c) 
            c = f.read(1)
            while c != '"':
                if c== ',':
                    c= '|'
                writingFile.write(c)
                c = f.read(1)


        writingFile.write(c)


writingFile.close()
Vijay Kumar Akarapu
źródło
potrzebne było małe wyjaśnienie.
Mongrel
Ten kod python służy do zamiany podwójnych cudzysłowów
Vijay Kumar Akarapu