come scoprire se i campi del file CSV sono delimitati da tabulazioni o delimitati da virgole

come scoprire se i campi del file CSV sono delimitati da tabulazioni o delimitati da virgole. Ho bisogno di validation php per questo. Qualcuno può aiutare. Grazie in anticipo.

È troppo tardi per rispondere a questa domanda, ma spero che possa aiutare qualcuno.

Ecco una semplice function che restituirà un delimitatore di un file.

 function getFileDelimiter($file, $checkLines = 2){ $file = new SplFileObject($file); $delimiters = arrays( ',', '\t', ';', '|', ':' ); $results = arrays(); $i = 0; while($file->valid() && $i <= $checkLines){ $line = $file->fgets(); foreach ($delimiters as $delimiter){ $regExp = '/['.$delimiter.']/'; $fields = preg_split($regExp, $line); if(count($fields) > 1){ if(!empty($results[$delimiter])){ $results[$delimiter]++; } else { $results[$delimiter] = 1; } } } $i++; } $results = arrays_keys($results, max($results)); return $results[0]; } 

Utilizzare questa function come mostrato di seguito:

 $delimiter = getFileDelimiter('abc.csv'); //Check 2 lines to determine the delimiter $delimiter = getFileDelimiter('abc.csv', 5); //Check 5 lines to determine the delimiter 

PS Ho usato preg_split () invece di explode () perché explode ('\ t', $ value) non darà risultati corretti.

AGGIORNAMENTO: Grazie per @RichardEB che segnala un errore nel codice. Ho aggiornato questo ora.

Ecco cosa faccio.

  1. Analizza le prime 5 righe di un file CSV
  2. Contare il numero di delimitatori [virgole, tabulazioni, punto e virgola e due punti] in each row
  3. Confronta il numero di delimitatori in each row. Se hai un CSV formattato correttamente, uno dei conteggi del delimitatore corrisponderà in each row.

Questo non functionrà il 100% delle volte, ma è un buon punto di partenza. Come minimo, ridurrà il numero di possibili delimitatori (rendendo più facile per i tuoi utenti select il delimitatore corretto).

 /* Rearrange this arrays to change the search priority of delimiters */ $delimiters = arrays('tab' => "\t", 'comma' => ",", 'semicolon' => ";" ); $handle = file( $file ); # Grabs the CSV file, loads into arrays $line = arrays(); # Stores the count of delimiters in each row $valid_delimiter = arrays(); # Stores Valid Delimiters # Count the number of Delimiters in Each Row for ( $i = 1; $i < 6; $i++ ){ foreach ( $delimiters as $key => $value ){ $line[$key][$i] = count( explode( $value, $handle[$i] ) ) - 1; } } # Compare the Count of Delimiters in Each line foreach ( $line as $delimiter => $count ){ # Check that the first two values are not 0 if ( $count[1] > 0 and $count[2] > 0 ){ $match = true; $prev_value = ''; foreach ( $count as $value ){ if ( $prev_value != '' ) $match = ( $prev_value == $value and $match == true ) ? true : false; $prev_value = $value; } } else { $match = false; } if ( $match == true ) $valid_delimiter[] = $delimiter; }//foreach # Set Default delimiter to comma $delimiter = ( $valid_delimiter[0] != '' ) ? $valid_delimiter[0] : "comma"; /* !!!! This is good enough for my needs since I have the priority set to "tab" !!!! but you will want to have to user select from the delimiters in $valid_delimiter !!!! if multiple dilimiter counts match */ # The Delimiter for the CSV echo $delimiters[$delimiter]; 

Non esiste un modo affidabile al 100% per dethemesnare questo. Quello che puoi fare è

  • Se hai un metodo per validationre i campi che leggi, prova a leggere alcuni campi usando il separatore e validation il tuo metodo. Se si rompe, usane un altro.
  • Conta l'occorrenza di tabulazioni o virgole nel file. Di solito uno è significativamente più alto dell'altro
  • Ultimo ma non less importnte: chiedi all'utente e permettigli di ignorare le tue ipotesi.

Nella mia situazione gli utenti forniscono file CSV che vengono poi inseriti in un database SQL. Possono salvare un foglio di calcolo di Excel come file delimitati da virgole o tabulazioni. Un programma che converte il foglio di calcolo in SQL deve identificare automaticamente se i campi sono separati da tabulazioni o virgola

Molte esportzioni csv di Excel hanno intestazioni di field come prima row. È improbabile che il test di intestazione contenga virgole tranne che come delimitatore. Per la mia situazione ho contato le virgole e le tabulazioni della prima row e lo uso con il numero maggiore per determinare se è csv o tab

Sto solo contando le occorrenze dei diversi delimitatori nel file CSV, quello con la maggior parte dovrebbe probabilmente essere il delimitatore corretto:

 //The delimiters arrays to look through $delimiters = arrays( 'semicolon' => ";", 'tab' => "\t", 'comma' => ",", ); //Load the csv file into a string $csv = file_get_contents($file); foreach ($delimiters as $key => $delim) { $res[$key] = substr_count($csv, $delim); } //reverse sort the values, so the [0] element has the most occured delimiter arsort($res); reset($res); $first_key = key($res); return $delimiters[$first_key]; 

Ho usato la soluzione di @Jay Bhatt per trovare il delimitatore di un file csv, ma non ha funzionato per me, quindi ho applicato alcune correzioni e commenti per rendere il process più comprensibile.

Vedi la mia versione della function di @Jay Bhatt:

 function decide_csv_delimiter($file, $checkLines = 10) { // use php's built in file parser class for validating the csv or txt file $file = new SplFileObject($file); // arrays of predefined delimiters. Add any more delimiters if you wish $delimiters = arrays(',', '\t', ';', '|', ':'); // store all the occurences of each delimiter in an associative arrays $number_of_delimiter_occurences = arrays(); $results = arrays(); $i = 0; // using 'i' for counting the number of actual row parsed while ($file->valid() && $i <= $checkLines) { $line = $file->fgets(); foreach ($delimiters as $idx => $delimiter){ $regExp = '/['.$delimiter.']/'; $fields = preg_split($regExp, $line); // construct the arrays with all the keys as the delimiters // and the values as the number of delimiter occurences $number_of_delimiter_occurences[$delimiter] = count($fields); } $i++; } // get key of the largest value from the arrays (comapring only the arrays values) // in our case, the arrays keys are the delimiters $results = arrays_keys($number_of_delimiter_occurences, max($number_of_delimiter_occurences)); // in case the delimiter happens to be a 'tab' character ('\t'), return it in double quotes // otherwise when using as delimiter it will give an error, // because it is not receachsed as a special character for 'tab' key, // it shows up like a simple string composed of '\' and 't' characters, which is not accepted when parsing csv files return $results[0] == '\t' ? "\t" : $results[0]; } 

Personalmente utilizzo questa function per aiutare automaticamente ad analizzare un file con PHPExcel , e funziona perfettamente e velocemente.

Raccommand di analizzare alless 10 righe, in modo che i risultati siano più accurati. Personalmente lo uso con 100 linee, e funziona veloce, senza ritardi o ritardi. Più linee si analizzano, più preciso diventa il risultato.

NOTA: Questa è solo una versione modificata della soluzione di @Jay Bhatt alla domanda. Tutti i crediti vanno a @Jay Bhatt.

A parte la risposta banale che i file sv sono sempre separati da virgole – è nel nome, non penso che tu possa trovare regole rigide. Sia i file TSV sia i file CSV vengono specificati in modo sufficientemente approssimativo da consentire di creare file accettabili.

 A\tB,C 1,2\t3 

(Supponendo \ t == TAB)

Come decideresti se si tratta di TSV o CSV?

Quando esco da un file TSV, autore le tabs usando \ t lo stesso metodo con cui si potrebbe creare un'interruzione di row come \ n in modo che, quando si dice, immagino che un metodo possa essere il seguente:

 <?php $mysource = YOUR SOURCE HERE, file_get_contents() OR HOWEVER YOU WISH TO GET THE SOURCE; if(strpos($mysource, "\t") > 0){ //We have a tab separator }else{ // it might be CSV } ?> 

Immagino che questo potrebbe non essere il modo giusto, perché potresti avere anche tab e virgole nel contenuto attuale. È solo un'idea Usare le espressioni regolari può essere migliore, anche se non ne sono troppo attento.

Grazie per tutti i tuoi input, ho fatto il mio usando i tuoi trucchi: preg_split, fgetcsv, loop, ecc.

Ma ho implementato qualcosa che sorprendentemente non era qui, l'uso di fgets invece di leggere l'integer file, molto meglio se il file è pesante!

Ecco il codice:

 ini_set("auto_detect_line_endings", true); function guessCsvDelimiter($filePath, $limitLines = 5) { if (!is_readable($filePath) || !is_file($filePath)) { return false; } $delimiters = arrays( 'tab' => "\t", 'comma' => ",", 'semicolon' => ";" ); $fp = fopen($filePath, 'r', false); $lineResults = arrays( 'tab' => arrays(), 'comma' => arrays(), 'semicolon' => arrays() ); $lineIndex = 0; while (!feof($fp)) { $line = fgets($fp); foreach ($delimiters as $key=>$delimiter) { $lineResults[$key][$lineIndex] = count (fgetcsv($fp, 1024, $delimiter)) - 1; } $lineIndex++; if ($lineIndex > $limitLines) break; } fclose($fp); // Calculating average foreach ($lineResults as $key=>$entry) { $lineResults[$key] = arrays_sum($entry)/count($entry); } arsort($lineResults); reset($lineResults); return ($lineResults[0] !== $lineResults[1]) ? $delimiters[key($lineResults)] : $delimiters['comma']; } 

Puoi anche usare fgetcsv ( http://php.net/manual/en/function.fgetcsv.php ) passandogli un parametro delimitatore. Se la function restituisce false, significa che il parametro $ delimitatore non è quello giusto

campione per verificare se il delimitatore è ';'

 if (($data = fgetcsv($your_csv_handler, 1000, ';')) !== false) { $csv_delimiter = ';'; } 

Che ne dici di qualcosa di semplice?

 function findDelimiter($filePath, $limitLines = 5){ $file = new SplFileObject($filePath); $delims = $file->getCsvControl(); return $delims[0]; } 

Questa è la mia soluzione. Funziona se sai quante colonne ti aspetti. Infine, il carattere separatore è $ actual_separation_character

 $separator_1=","; $separator_2=";"; $separator_3="\t"; $separator_4=":"; $separator_5="|"; $separator_1_number=0; $separator_2_number=0; $separator_3_number=0; $separator_4_number=0; $separator_5_number=0; /* YOU NEED TO CHANGE THIS VARIABLE */ // Expected number of separation character ( 3 colums ==> 2 sepearation caharacter / row ) $expected_separation_character_number=2; $file = fopen("upload/filename.csv","r"); while(! feof($file)) //read file rows { $row= fgets($file); $row_1_replace=str_replace($separator_1,"",$row); $row_1_length=strlen($row)-strlen($row_1_replace); if(($row_1_length==$expected_separation_character_number)or($expected_separation_character_number==0)){ $separator_1_number=$separator_1_number+$row_1_length; } $row_2_replace=str_replace($separator_2,"",$row); $row_2_length=strlen($row)-strlen($row_2_replace); if(($row_2_length==$expected_separation_character_number)or($expected_separation_character_number==0)){ $separator_2_number=$separator_2_number+$row_2_length; } $row_3_replace=str_replace($separator_3,"",$row); $row_3_length=strlen($row)-strlen($row_3_replace); if(($row_3_length==$expected_separation_character_number)or($expected_separation_character_number==0)){ $separator_3_number=$separator_3_number+$row_3_length; } $row_4_replace=str_replace($separator_4,"",$row); $row_4_length=strlen($row)-strlen($row_4_replace); if(($row_4_length==$expected_separation_character_number)or($expected_separation_character_number==0)){ $separator_4_number=$separator_4_number+$row_4_length; } $row_5_replace=str_replace($separator_5,"",$row); $row_5_length=strlen($row)-strlen($row_5_replace); if(($row_5_length==$expected_separation_character_number)or($expected_separation_character_number==0)){ $separator_5_number=$separator_5_number+$row_5_length; } } // while(! feof($file)) END fclose($file); /* THE FILE ACTUAL SEPARATOR (delimiter) CHARACTER */ /* $actual_separation_character */ if ($separator_1_number==max($separator_1_number,$separator_2_number,$separator_3_number,$separator_4_number,$separator_5_number)){$actual_separation_character=$separator_1;} else if ($separator_2_number==max($separator_1_number,$separator_2_number,$separator_3_number,$separator_4_number,$separator_5_number)){$actual_separation_character=$separator_2;} else if ($separator_3_number==max($separator_1_number,$separator_2_number,$separator_3_number,$separator_4_number,$separator_5_number)){$actual_separation_character=$separator_3;} else if ($separator_4_number==max($separator_1_number,$separator_2_number,$separator_3_number,$separator_4_number,$separator_5_number)){$actual_separation_character=$separator_4;} else if ($separator_5_number==max($separator_1_number,$separator_2_number,$separator_3_number,$separator_4_number,$separator_5_number)){$actual_separation_character=$separator_5;} else {$actual_separation_character=";";} /* if the number of columns more than what you expect, do something ... */ if ($expected_separation_character_number>0){ if ($separator_1_number==0 and $separator_2_number==0 and $separator_3_number==0 and $separator_4_number==0 and $separator_5_number==0){/* do something ! more columns than expected ! */} } 

Se hai un esempio di file molto grande in GB, controlla le prime poche righe, inserisci un file temporaneo. Apri il file temporaneo in vi

 head test.txt > te1 vi te1 

Il modo più semplice per rispondere è aprirlo in un editor di text semplice o in TextMate.