CSV fileba gyomlálás

Tiszteletem az uraknak,
egy szöveges csv állományban ahol a mezők opcionálisan idézőjelek között vannak (") néha egy szöveges mezőben is előfordulás az idézőjel.
Nem kell mondanom ezzel az egész feldolgozást padlóra küldve.
Íme egy példa ahol a 4. mezőben a "C" rész a hunyó:
"AAA";"BBBB";"D";"COFFEE "C" FUT.";1.;"USD";.00000000000;"USD";"7000";""

Egy lehetséges megoldás hogy lecserélem a problémás helyen (és csak ott) a `"` jelet pl -> `'`-re.
Pontosabban ahol nem `;"` így vagy `";` így jelentkezik plusz a sor vége és eleje (ahol úgy csak `"` önmagába fordul elö) kivétel a többi helyen cserélendő.
Na ezt meg lehet egy egy soros sed-del csinálni?

Köszönöm a tippeket előre is.
Üdv
MZ.

Hozzászólások

Szervusz !

- a legjobb dolog az, ha a kiindulási konvertálást tudod irányítani
- ebben a helyzetben egy szövegszerkesztő csere funkcióját hívnám segítségül: ";" csere ;" csere, "; csere.
(De SED-re is írhatsz scriptet a fenti hármas szabályt figyelembe véve.

saját magam tabulátoros mezőelválasztást használok; a más mezőszeparátor TAB-ra változtatására írtam (linux alatt) egy primitív C programocskát.

CSZ

inkabb ilyesmit kellene csinalni, hogy:


awk 'BEGIN { FS=";"; } { ... }'

szoval hogy az awk-ban a field szepara'tor (FS) legyen a pontosvesszo", igy ott vagja szet a sorokban a mezoket ahol szeretnenk, mindegyik mezo"t lekezeled ugy ahogy tudod/akarod/kell, es akkor jo lesz.

szoval awk, inkabb, azt pont erre talaltak ki lenyegeben. sed-ben is biztos meg lehet csinalni, de igy joval egyszerubb.

pontosan egyebkent nem ertem mit takar(hat) ez a "feldolgozas", igy ennel konkretabbat nem nagyon tudok irni...

Ha jobb nem lesz:


sed -e "s/\"\([[:alnum:][:space:]]*\)\"\([[:alnum:]]*\)\"/\"\1'\2'/g" minta.txt

Kieg.: Ha biztosan nem lehet az érintett helyen szám, akkor az "alnum" helyett "alpha" is elég.

2. kieg. Ha biztosan ilyen alakú: 'szóköz"betűk"szóköz', akkor:


sed -e "s/\([[:space:]]\)\"\([[:alpha:]]*\)\"\([[:space:]]\)\(\)/\1'\2'\3/g" minta.txt

-----
"Én vagyok a hülye, hogy leállok magával vitatkozni."

egynelöre nincs jobb.

Ugy látszik én bonyolitottam tul a dolgot.
Én ugy képzeltem hogy ki kell zárni a sor eleji (^) és végi($) sima idézöjelekt és utánn kell keresni a kivételekt.
Ha jól értem te egyszerüen a xxx"xxx"xxx mintát (amibe nincs ;)keresed és cseréled.
Csak egy dolog ne világos a szintaxisban, az uj mintadefinicio :\1'\2'\3
A számok mit jelentenek?

Köszi mégegyszer.

A zárójelek közötti stringeket rendre a \1, \2 és a \3 jelöli. Tehát a \1 az első zárójelpár közzé zárt sztring, a \2 a második zárójelpár közzé zárt sztring, ... Pontosabban az adott mintára illeszkedő találatot jelenti.

-----
"Én vagyok a hülye, hogy leállok magával vitatkozni."

Ez viszont beégetve használja ugyan a separátor ";"-ket, de elvileg jól cserelé a hibás (páratlan) "-ket mezőn belől (mármint a mező végén lezárja és + '-t illeszt be.):

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

Csak mert sed-hez is kedvem volt, nem csak AWK-hoz :-)

Sed-del szerintem nem (iiletve csak csúnya regexppel :-D), de awk-val szebb:

awk 'BEGIN {FS=";"} { for (i=1; i<=NF; i++) { printf("%s;",gensub("^(\"[^\"]+)\"([^\"]+)\"([^\"]+\")$","\\1'"'"'\\2'"'"'\\3","",$i))}printf("\n","") }'

A kedvencem az ' escapelése ;-)

Azzal az energiaval amit az egy sorba paszirozasra (es annak bugfixelesere) forditasz, lehetne egy rovidke amde korrekt parsert irni ra barmelyik scriptnyelven. :-/

Ezt is belátom, de a környezet adott és nincs mindenféle scritnyelv installálva (még perl sem).
Volt egy konkrét parser javában irva, az kb. 4 órát molyolt rajta, de mivle kétoránkét jönnek az uj adatok ez a megoldás nem volt járható.

Mivel van egy shell szürö most elétéve, kézenfekvö hogy a hibás sorokat is igy gyomláljam ki.

Ez így hibás CSV, ha " szerepel az adatban, akkor azt duplázni kell. Ha tudod módosítani a CSV generálást, akkor érdemes ezt berakni, és nem fog borulni a feldolgozás.
pl:
"123";"monitor 20"" tft";";ez adat!;"

Ha nem, akkor maradnak azok a megoldások, amit a kollégák írtak.

http://search.cpan.org/~makamaka/Text-CSV-1.21/lib/Text/CSV.pm

"In case there is really bad CSV data, [...] there is a way to get that parsed, and leave the quotes inside the quoted field as-is. This can be achieved by setting allow_loose_quotes AND making sure that the escape_char is not equal to quote_char."


#!perl

use 5.010;
use strict;
use warnings;

use Text::CSV;
use Data::Dumper;

my $csv = Text::CSV->new(
  { sep_char           => ';',
    allow_loose_quotes => 1,
    escape_char        => '\\',
  }
);

my $status = $csv->parse(
  '"AAA";"BBBB";"D";"COFFEE "C" FUT.";1.;"USD";.00000000000;"USD";"7000";""');
my @columns = $csv->fields;

warn Dumper( $status, \@columns );

Output:


$VAR1 = 1;
$VAR2 = [
          'AAA',
          'BBBB',
          'D',
          'COFFEE "C" FUT.',
          '1.',
          'USD',
          '.00000000000',
          'USD',
          '7000',
          ''
        ];

vagy:


#!/usr/bin/python
import sys
f = open(sys.argv[2],'w')
for line in open(sys.argv[1]).readlines():
    f.write(";".join([x.lstrip('"').rstrip('"').replace('"','\'') for x in line.split(';')]))