Hibás csv beolvasása

Hibás csv beolvasása
2018-06-21T14:02:01+02:00
2018-06-26T20:05:57+02:00
2022-08-11T04:35:30+02:00
*deleted_86291744
Sziasztok!

Kaptam egy olyan .csv fájlt(ez csak példa), amiben a mezők tartalmazhatnak " karaktert escape karakter nélkül, annak ellenére, hogy ugyanezzel a karakterrel vannak körbezárva a mezők(amik tartalmazhatnak sortöréseket).

A fgetcsv függvény nem működik rajta megfelelően, attól függetlenül, hogy egyértelműen látszik a linkelt példafájlban, hogy az "Inox" résznél az "-ok nem zárnak körbe mezőt, hanem a mező tartalmában vannak.

Ezzel a kóddal:

if (($handle = fopen($filePath, "r")) !== false) { while (($item = fgetcsv($handle, 10000, ',', '"')) !== false) { echo $item[0] . '<br>'; } }

Ezt kapom:

1 1 1 a ez nem jó" 1 1
És ezt szeretném:

1 1 1 a 1 1
Erre van valami egyszerűbb megoldást, mint hogy megírom magamnak a csv beolvasóm, ami azzal jár, hogy majdnem karakterenként végig kell nézni a szöveget? 

Lehet más eset, amikor nem lehet eldönteni, hogy ténylegesen a mező végét jelöli-e egy " mikor mező tartalmának szánták, ezeken kívül?
1. sor végén és következő sor elején is megtalálható
2. vessző mellett van

Jelenleg egyik esettel sem találkoztam a fájlban.
Mutasd a teljes hozzászólást!
Én azzal az elvvel bontanám fel a fájlt, hogy sorvégének nem a CR-LF párost tekinteném, hanem az azt megelőző és az azt követő idézőjelet is, mezőhatárolónak pedig az idézőjel-vessző-idézőjel hármast.

Működő kód a mintafájlodra:

<?php $content = file_get_contents('anyagok2.csv'); $content = trim($content, "\x22\r\n"); $lines = explode("\x22\x0d\x0a\x22", $content); foreach ($lines as $line_id => $line) { $fields = explode("\x22,\x22", $line); foreach ($fields as $field_id => $field) { echo('sor: '.$line_id.'; oszlop: '.$field_id.'; ertek: '.$field."\r\n"); } }
Mutasd a teljes hozzászólást!

  • Szia!
    Van egy triviális ötletem, ha soronként végigmész a szövegen, akkor a mezők száma alapján tudod, hogy x db " karakternek kellene lennie, ezzel esetleg lehet szűrni, hogy az adott sor az jó-e.
    Ha definiálható az összes olyan helyzet, ahol a " karakter a szöveg része, és nem mezőhatároló, akkor cserélgesd ki az összes helyen valami karakterre, olvasd be a csv-t, és az eredményen cseréld vissza a karakter.
    Én pontosvesszőkkel jártam így egyszer.
    Üdv!
    G.
    Mutasd a teljes hozzászólást!
  • Szia!

    Sajnos lehet a mezőkben sortörés is.
    Mutasd a teljes hozzászólást!
  • Jogos, miután elküldtem a választ, leesett,  hogy írtad. De ettől még mehet az ötletem, annyi kiegészítéssel, hogy az adott sort a következővel, vagy következőkkel együtt kell vizsgálnod, amíg meg nincs a minimum darabszáma a " karakternek.
    Mutasd a teljes hozzászólást!
  • Kérdések, mivel nem csatoltál minta csv-fájlt:
    1. Ha vessző a limiter, akkor hogy működik, mivel én a harmadik sor utolsó elemében látok egyet?
    2. Ha az viszont pont és nem vessző, akkor próbáld ki a következőt a fájloddal

    <?php function csv_to_array($filename='', $delimiter=',') { if( !file_exists($filename) || !is_readable($filename) ) return false; $header = NULL; $data = array(); if (($handle = fopen($filename, 'r')) !== false) { while (($row = fgetcsv($handle, 1000, $delimiter)) !== false) { if(!$header) $header = $row; else $data[] = array_combine($header, $row); } fclose($handle); } return $data; } $adat = csv_to_array('file.csv',','); var_dump($adat); ?>
    Csatoltam az általam próbált tesztfájlt...
    Mutasd a teljes hozzászólást!
    Csatolt állomány
  • Ítt pedig a futtatás eredménye...
    Mutasd a teljes hozzászólást!
  • Szia!

    Csatoltam példa csv fájlt, le lehet tölteni.

    Azzal van gond, ha így néz ki egy mező: "ez egy"idezojeles" mezo". Az én példa fájlomban a mezők körül vannak véve " karakterrel. És az a problémám, hogy a mező is tartalmaz ilyen karaktert, escape nélkül.
    Mutasd a teljes hozzászólást!
  • Sajnos lehet a mezőkben sortörés is.

    Az önmagában még nem gond, de a kezeletlenül beágyazott idézőjelek már igen (PHP: fgetcsv - Manual  szerint vagy duplázni kéne, vagy escape-elni, és akkor már menne).
    Egy ilyen nálam működik, csak a 14 miatt eléggé bedrótozott hatást kelt:

    <?php $file=fopen("anyagok2.csv","r"); while (($line = fgets($file)) !== false) { while(count($trallala=str_getcsv($line))<14) $line.=fgets($file); echo $trallala[0]." ".count($trallala)."\n"; } fclose($file);
    Mutasd a teljes hozzászólást!
  • Én azzal az elvvel bontanám fel a fájlt, hogy sorvégének nem a CR-LF párost tekinteném, hanem az azt megelőző és az azt követő idézőjelet is, mezőhatárolónak pedig az idézőjel-vessző-idézőjel hármast.

    Működő kód a mintafájlodra:

    <?php $content = file_get_contents('anyagok2.csv'); $content = trim($content, "\x22\r\n"); $lines = explode("\x22\x0d\x0a\x22", $content); foreach ($lines as $line_id => $line) { $fields = explode("\x22,\x22", $line); foreach ($fields as $field_id => $field) { echo('sor: '.$line_id.'; oszlop: '.$field_id.'; ertek: '.$field."\r\n"); } }
    Mutasd a teljes hozzászólást!
  • Esetleg ez:
    Szerk, inkább csatolom...

    (Én fejéhez vágnám annak aki ilyen CSV -t ad át.)
    Mutasd a teljes hozzászólást!
    Csatolt állomány
  • H.Tibor.87,
    a kódodat lefuttatva a kérdező által csatolt anyagok2.csv-n, a [3][12]-es elemre rossz értéket ad vissza.
    Az eredeti mezőérték:
    abc szöveg asd"Inox"asd szöveg abc</p> <p>ez nem jó
    A te kódod által visszaadott mezőérték:
    abc szöveg asdInox"asd szöveg abc</p> <p>ez nem jó"
    Vagyis az asd"Inox közül lenyeli az idézőjelet, a szöveg végén pedig meghagyja azt, ami mezőhatároló lenne.
    Mutasd a teljes hozzászólást!
  • Mint mondtam, én a fejéhez vágnám annak aki ilyen-re mondja azt hogy "CSV".
    És még kitudja mennyi apróság lehet ami miatt borul az egész...
    Mutasd a teljes hozzászólást!
  • Szia!

    Én is, de sajnos az én érdekem is, hogy múködjön. Egyébként olyan több ezer soros csv-ről van szó, amit valószínűleg 10-100+ ember használhat napi szinten.


    bonga: holnap kipróbálom amit küldtél.
    Mutasd a teljes hozzászólást!
  • Ha több ezer soros csv-ről van szó, akkor a file_get_contents()-el egyben benyalni a memóriába az egész adatfájlt lehet kicsit memóriaigényes lesz, de a "\r\n" 4 byte-nyi jelsorozatot sortörésnek értelmezve lehetne soronként is olvasni a fájlt és az így kapott sorokat explode()-olva a "," jelhármassal, majd az eredményt helyére pakolva a memóriaprobléma is kiküszöbölhető.
    Mutasd a teljes hozzászólást!
  • erdekes, mert, ha Libre Office-el nyitom meg, az jol tagolja, utana eleg ujra menteni csv-be es mar helyes lesz
    Mutasd a teljes hozzászólást!
  • A teljes topic újraolvasása után ("a készítő kezének levágásával most egyet-értve") szerintem ha tevemadar javaslatát továbbgondolod, akkor "ezen hibás fájl"-al is lehet valamit kezdeni (LibOff nélkül is):

    <h2>CSV beolvasása PHP-vel (albert75)</h2> <?php $new_arr = array(); $file2 = fopen("anyagok2.csv","r"); echo '<textarea cols="100" rows="8" style="text-align:left;">'; while (($line2 = fgets($file2)) !== false) { while( count($trallala2 = str_getcsv($line2) ) < 14 ) { $line2 .= fgets($file2); } if( count($trallala2) >= 14 ) { array_push( $new_arr, $line2 ); echo $line2; } } fclose($file2); echo '</textarea>'; echo '<br><br>'; echo '<table align="center" border="1">'; for( $i=0;$i<count($new_arr);$i++ ) { $lines_arr = explode(",", $new_arr[$i]); echo '<tr>'; for( $j=0;$j<count($lines_arr);$j++ ) { echo '<td>'.$lines_arr[$j].'</td>'; } echo '</tr>'; } echo '</table>'; echo '<br>'; // mivel neked ez az sk-függvényem valszeg nincsen meg ezért továbbiak kommentezve //echo '<div class="my_dump">'; //echo my_var_dump($new_arr); //echo '</div><br>'; ?>
    Megj: , hogy a fájlod letölthetőségét elsőre nem láttam, de ez a teszt már avval készült...
    Mutasd a teljes hozzászólást!
  • Szia!

    Úgy tűnik teljesen jó a kód. :) Ha biztos leszek benne, elfogadom.

    Még annyival szebb lesz a dolog, hogy vegyesen volt CR LF és csak LF. :) De azt már megoldom. 

    Ettől még mindig idegesít, hogy miért nem lehet szabvány formában kiadni az adatot. :/ Biztos nem csak én futottam bele ebbe.
    Mutasd a teljes hozzászólást!
  • Neee, milyen szoftver csinálja azt, hogy vegyesen alkalmazza a CRLF-et és az LF-et sorvége karakterként?
    Vagy több forrásból származó csv van egybefűzve és azt kellene egyben feldolgozni?
    Hmmm... Na ezek az élet apró örömei, remélem sikerül jó, egyszerű, hatékony és gyors megoldást találni. :)
    Mutasd a teljes hozzászólást!
  • Nem tudom. A mező tartalmában van csak LF, szóval gondolom úgy lett lementve a tartalma, a program pedig ami csinálja a csv-t CR LF-et használ.
    Mutasd a teljes hozzászólást!
  • Javaslom, ahol a CSV -t generálják, ott kell javítani, nem a feldolgozással szenvedni.
    Ha van valamelyik cella tartalmába "," karakterek, akkor borul az egész...
    ha szabványos CSV -t generálnának, mindenkinek megkönnyítené a dolgát.
    Mutasd a teljes hozzászólást!
  • Azért én kíváncsi lennék az elfogadott javaslat alapján, az általad elkészített és működő megoldás LINK-jére is...
    Megj: , az én linkem nem jó helyre mutat...
    Mutasd a teljes hozzászólást!
  • Addig a jó, amíg így marad - bonga megoldása egy kicsit is normálisabb CSV-re már nem működik (felesleges idézőjelek elhagyása, akár csak az egy darab üres elem esetén).
    Mutasd a teljes hozzászólást!
  • Hát igen.  A hibás fájlt a küldő fél fejére kell borítani az első alkalommal, nem pedig bátorítani azzal, hogy megpróbáljuk a rossz fájlt feldolgozni.
    Mutasd a teljes hozzászólást!
  • Szia!

    Minden esetben körül van véve a mező " karakterekkel, emiatt működik a megoldása. Ami érdekes, mert LibreOffice nem minden hová tesz.

    Az elfogadott választ használtam egy az egyben, de később leszavaztak és most át konvertálom LibreOffice-al, utána pedig működik az fgetcsv.
    Mutasd a teljes hozzászólást!
  • Minden esetben körül van véve a mező " karakterekkel, emiatt működik a megoldása.

    Igen, ezt mondtam én is. De ha akár csak az ""-t lehúzod a 4-ik rekord végéről, akkor már rögtön csak 5 rekordod lesz, köztük egy 26 elemű (2 elemnek meg még nyoma is vész). Pedig CSV-ben az üres elemet alapból úgy jelölik, hogy nem írnak oda semmit. Egy olyan sor, hogy , két üres elemet tartalmaz, egyet a vessző előtt, egyet meg mögötte.

    Ami érdekes, mert LibreOffice nem minden hová tesz.

    Ha most az az érdekes, hogy így is működik a LibreOffice-vel feljavított CSV-kre: ezt nem tartom valószínűnek, a fentiek miatt fejre fog állni
    Ha most az az érdekes, hogy a LibreOffice nem mindenhova tesz idézőjeleket: persze, hogy nem, hiszen a CSV vesszőkkel elválasztott elemekről szól. Idézőjelekre egyedül akkor van szükség, ha valami speciális cuccot akarsz becsomagolni (pl. vesszőt vagy sortörést tartalmazó mező).
    Mutasd a teljes hozzászólást!
  • Az elfogadott választ használtam egy az egyben, de később leszavaztak és most át konvertálom LibreOffice-al

    Senki nem szavazott le, csak jelezte, hogy elég egy kis eltérés a következő CSV-fájlban és már bukik is az egész (a következő ilyen CSV-fájlt is konvertálni fogod LibOff-al?)...
    Mutasd a teljes hozzászólást!
  • Nem, úgy értem akinek csinálom azt kérte, hogy az office-os megoldás legyen.

    Igen, ha nincs rá jobb megoldás. Mivel be kell olvasnom a fájlt, és még nem csinálták meg helyes formára, így ez az egyetlen amit tenni tudok. Szívás, de ez van.
    Mutasd a teljes hozzászólást!
  • Ha sikerülne a kapott fájlt eredeti állapotában "hibátlanul" beolvasni, akkor mit kell csinálni vele?
    Esetleg olyan "állapotba" hozni hogy DB-be iportálható legyen?
    Talán egy "javított" példányt kell belőle készíteni (lásd "2018.06.22. 12:26"-i linkem, melyet csak a te CSV-fájloddal próbáltam, mivel más hibás nem áll rendelkezésemre)?
    Azaz mi a cél a fájl feldolgozásával?
    Mutasd a teljes hozzászólást!
  • Mivel valószínű el vagy foglalva a "LibreOffice"-os ellenőrzésekkel és nem volt időd reagálni, ezért készítettem egy univerzálisabb tesztet, melynél:
    1. Fel lehet tölteni egy CSV-fájl (max 100Kbyte).
    2. A program elsőre fájlt teszteli a beolvasás után és ha lehet kijelzi a hibát.
    3. Ha megfelelőnek tűnik, akkor megjeleníti soronként egy TEXTARE elemben és konvertálja egy kétdimenziós tömbbe (a tömböt is megjeleníti).
    4. Kövekező lépésként a konvertált tömbből megjeleníti egy TABLE elemben a sorok/adatelemek tartalmát.
    5. Végezetül elmenti egy új CSV-fájlba, amelyet már könnyen lehet importálni egy DB-be.
    Ha valakinek van elég ideje és néhány CSV-fájlja az próbálja ki tapasztalatszerzés céljából....
    Megj: Aki ezt megteszi és jelzi is a tapasztalatát PRIVÁT üzenetben, annak előre is -nöm...
    Kieg: A feltöltött fájlok 15 perc után automatikusan törlődnek...
    Mutasd a teljes hozzászólást!
  • Szia!

    Adatbázisba menteni. De egyébként mindegy mi a cél vele, a lényeg az volt, hogy be tudjam olvasni, onnantól pedig akár mit is lehet vele csinálni. :)
    Mutasd a teljes hozzászólást!
abcd