2015-05-27 19 views
6

quando ho esplodere file CSV sul delimitatore (;) il esplodere con successo in alcuni eccellono programma e fallito in altriesplode file csv su delimitatore (;) e delimitatore (,)?

anche quando esplodo file CSV sul delimitatore (,) il esplodere con successo in alcuni eccellono programma e non è riuscito a altri

Come posso esplodere in tutte le versioni di Excel? Come posso sapere il delimitatore perfetto da far esplodere?

sì, c'è il codice ..

if (!function_exists('create_csv')) { 
    function create_csv($query, &$filename = false, $old_csv = false) { 
     if(!$filename) $filename = "data_export_".date("Y-m-d").".csv"; 
     $ci = &get_instance(); 
     $ci->load->helper('download'); 
     $ci->load->dbutil(); 
     $delimiter = ";"; 
     $newline = "\r\n"; 
     $csv = "Data:".date("Y-m-d").$newline; 
     if($old_csv) 
      $csv .= $old_csv; 
     else 
      $csv .= $ci->dbutil->csv_from_result($query, $delimiter, $newline); 
     $columns = explode($newline, $csv); 
     $titles = explode($delimiter, $columns[1]); 
     $new_titles = array(); 
     foreach ($titles as $item) { 
      array_push($new_titles, lang(trim($item,'"'))); 
     } 
     $columns[1] = implode($delimiter, $new_titles); 
     $csv = implode($newline, $columns); 
     return $csv; 
    } 
} 

a volte ho messo $ delimitatore = ";"; e sometims $ delimiter = ",";

grazie ..

+2

Non è possibile. Il delimitatore utilizzato da qualsiasi istanza MS Excel è specifico della locale, in genere in base al fatto che il Paese per il quale è configurato utilizzi un ',' o un '.' come separatore decimale ..... a'; 'è più comune, ma non c'è una risposta universale ... se ci fosse, non avresti nemmeno bisogno di chiedere –

+0

È una delle ragioni per cui CSV non è un buon formato da usare in alternativa ai formati nativi di Excel –

+0

Hai del codice da mostrare noi? Stai cercando di leggere il file e poi esplodere le linee, o stai usando fgetcsv? – foxbeefly

risposta

1

È possibile utilizzare funzione di supporto per rilevare meglio delimitatore come:

public function find_delimiter($csv) 
{ 
    $delimiters = array(',', '.', ';'); 
    $bestDelimiter = false; 
    $count = 0; 
    foreach ($delimiters as $delimiter) 
     if (substr_count($csv, $delimiter) > $count) { 
      $count = substr_count($csv, $delimiter); 
      $bestDelimiter = $delimiter; 
     } 
    return $bestDelimiter; 
} 
0

non c'è modo per essere sicuri al 100% si prendono di mira il vero delimitatore. Tutto quello che puoi fare è indovinare.

Si dovrebbe iniziare trovando il delimitatore destro, quindi esplodere il CSV su questo delimitatore.

Per trovare il delimitatore, in sostanza, si desidera una funzione che conta il numero di , e il numero di ; e che restituisce il maggiore.

Qualcosa di simile:

$array = explode(find_delimiter($csv), $csv); 

speriamo vi sia utile;)

Edit: La funzione find_delimiter potrebbe essere qualcosa di simile:

function find_delimiter($csv) 
{ 
    $arrDelimiters = array(',', '.', ';'); 
    $arrResults = array(); 
    foreach ($arrDelimiters as $delimiter) 
    { 
     $arrResults[$delimiter] = count(explode($delimiter, $csv)); 
    } 
    $arrResults = rsort($arrResults); 
    return (array_keys($arrResults)[0]); 
} 
0

La risposta breve è, probabilmente non è possibile a meno che non sia possibile applicare un certo euristico per determinare il formato del file. Se non sai e non riesci a rilevare il formato del file che stai analizzando, l'analisi sarà difficile.

Tuttavia, una volta determinato (o richiesto uno particolare) il formato del delimitatore. Probabilmente troverai che lo fgetcsv integrato di php sarà più semplice e preciso di una strategia basata su manuale explode.

0

Bene, sembra che tu sappia esattamente che il tuo delimitatore sarà "," o ";". Questo è un buon punto di partenza. Pertanto, si può provare a sostituire tutte le virgole (,) in punti e virgola (;), quindi esplodere solo con il punto e virgola. Tuttavia, in questo approccio avresti sicuramente un problema in alcuni casi, perché alcune linee dei tuoi file CSV potrebbero essere così:

"nome, valore", altro nome, altro valore, cognome, ultimo valore

In questo modo, il delimitatore del file CSV sarà virgola se nel file CSV ci saranno quattro colonne. Tuttavia, cambiando virgole in punto e virgola otterresti cinque colonne che sarebbero errate. Quindi, cambiare un delimitatore in un altro non è un buon modo.

Tuttavia, se il file CSV è formattato correttamente, è possibile trovare il delimitatore corretto in qualsiasi riga.Quindi, puoi provare a creare alcune funzioni come find_delimiter ($ csvLine) come proposto da @johnkork, ma il problema con questo è che la funzione stessa non può sapere quale delimitatore cercare. Tuttavia, conosci esattamente tutti i possibili delimitatori, quindi potresti provare a creare un'altra funzione abbastanza simile a delimiter_exists ($ csvLine, $ delimiter) che restituisce true o false.

Ma anche la funzione delimiter_exists ($ csvLine, $ delimiter) non è sufficiente. Perché? Perché per l'istanza della linea CSV sopra riportata otterresti sia "," che ";" sono delimitatori che esiste. Per la virgola sarebbe CSV con quattro colonne e per il punto e virgola sarebbero due colonne.

Quindi, non esiste un modo universale per ottenere esattamente ciò che si desidera. Tuttavia, potrebbe esserci un altro modo per verificare: la prima riga del file CSV che è l'intestazione se i tuoi file CSV hanno un'intestazione. Principalmente, le intestazioni nel file CSV non hanno (non necessariamente) altri simboli, ad eccezione dei nomi alfanumerici delle colonne, che sono delimitati dallo specifico delimitatore. Quindi, si può tentare di creare la funzione come delimiter_exists ($ csvHeader, $ delimitatore) la cui attuazione potrebbe essere simile a questo:

function delimiter_exists($csvHeader, $delimiter) { 
    return (bool)preg_match("/$delimiter/", $csvHeader); 
} 

Per voi caso specifico si può usare in questo modo:

$csvHeader = "abc;def"; 
$delimiter = delimiter_exists($csvHeader, ',') ? ',' : ';'; 

Speranza questo aiuta!

+0

In realtà, la funzione find_delimiter è in grado di cercare un elenco illimitato di potenziali delimitatori (vedere la matrice $ arrDelimiters). Passare attraverso la sezione di intestazione del file CSV come hai detto è una buona idea, dato che migates le celle "false positive" come con numeri con decimali e virgole. :) – johnkork

1

Se si ha un'idea dei dati previsti (numero di colonne), questo potrebbe funzionare come ipotesi e potrebbe essere una buona alternativa al confronto che si verifica maggiormente (a seconda del tipo di dati che ci si aspetta). Funzionerebbe ancora meglio se si avesse un record di intestazione, immagino. (Potresti inserire un assegno per specifici valori di intestazione)

Ci scusiamo per non averlo inserito nel tuo codice, ma non sono proprio sicuro di quali chiamate stai facendo, ma dovresti essere in grado di adattarlo.

$expected_num_of_columns = 10; 
$delimiter = ""; 

foreach (array(",", ";") as $test_delimiter) { 
    $fid = fopen ($filename, "r"); 
    $csv_row = fgetcsv($fid, 0, $test_delimiter); 
    if (count($csv_row) == $expected_num_of_columns) { 
     $delimiter = $test_delimiter; 
     break; 
    } 
    fclose($fid); 
} 

if (empty($delimiter)) { 
    die ("Input file did not contain the correct number of fields (" . $expected_num_of_columns . ")"); 
} 

Non utilizzare questo se, ad esempio, tutti o quasi tutti i campi contengono numeri non interi (ad esempio un elenco di importi monetari) e non ha alcun record di intestazione, perché i file separati da ; sono più probabili per utilizzare , come punto decimale e potrebbe esserci lo stesso numero di virgole e punti e virgola.

Problemi correlati