2010-08-03 22 views

Respuesta

8

No hay una manera 100% confiable de determinar esto. Lo que puede hacer es

  • Si tiene un método para validar los campos que lee, intente leer algunos campos usando el separador y valide contra su método. Si se rompe, usa otro.
  • Cuente la aparición de pestañas o comas en el archivo. Por lo general, uno es significativamente más alto que el otro
  • Por último, pero no por ello menos importante: pregúntele al usuario y permítale que anule sus suposiciones.
0

Aparte de la respuesta trivial de que c archivos sv están siempre separados por comas - está en el nombre, no creo que se puede llegar a ninguna regla dura. Los archivos TSV y CSV están lo suficientemente especificados como para que pueda encontrar archivos que serían aceptables.

A\tB,C 
1,2\t3 

(Suponiendo \ t == TAB)

¿Cómo se decide si se trata de TSV o CSV?

+1

La c puede representar 'carácter', lo que hace,; o \ t todas las opciones válidas. – julesj

+0

@julesj Una búsqueda rápida sugiere muy pocas usando el carácter c para referirse a la mayoría de los significados. Aunque incluso entonces, el separador no es siempre una coma, solo para complicar las cosas. –

0

Cuando salida de un archivo TSV Me autor las pestañas usando \ t el mismo método que uno autor de un salto de línea como \ n para que se decía que supongo un método podría ser el siguiente:

<?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 
} 
?> 

I Adivina que esta puede no ser la manera correcta, porque también podrías tener pestañas y comas en el contenido real. Es solo una idea Usar expresiones regulares puede ser mejor, aunque no estoy muy al tanto de eso.

-3

La forma más fácil de responder esto es abrirlo en un editor de texto plano, o en TextMate.

+0

Esto no se aplica en absoluto a la pregunta formulada. – Jacta

3

En mi situación, los usuarios proporcionan archivos csv que luego se ingresan en una base de datos SQL. Pueden guardar una hoja de cálculo de Excel como archivos delimitados por comas o tabuladores. Un programa que convierte la hoja de cálculo a SQL necesita identificar automáticamente si los campos están separados por tabulaciones o por comas

Muchas exportaciones de Excel csv tienen encabezados de campo como primera línea. Es poco probable que la prueba de encabezado contenga comas, excepto como delimitador. Para mi situación conté las comas y las pestañas de la primera línea y la uso con el número mayor para determinar si es csv o pestaña

10

Esto es lo que hago.

  1. analizar los primeros 5 líneas de un archivo CSV
  2. contar el número de delimitadores [comas, pestañas, punto y coma y dos puntos] en cada línea
  3. comparar el número de delimitadores en cada línea. Si tiene un CSV formateado correctamente, uno de los conteos de delimitador coincidirá en cada fila.

Esto no funcionará el 100% del tiempo, pero es un punto de partida decente. Como mínimo, reducirá el número de delimitadores posibles (lo que facilita a los usuarios seleccionar el delimitador correcto).

/* Rearrange this array to change the search priority of delimiters */ 
$delimiters = array('tab'  => "\t", 
       'comma'  => ",", 
       'semicolon' => ";" 
       ); 

$handle = file($file); # Grabs the CSV file, loads into array 

$line = array();   # Stores the count of delimiters in each row 

$valid_delimiter = array(); # 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]; 
+1

Funciona bien. Lo usé como una función antes de analizar mis archivos. El valor de retorno de la misma, ** $ delimitadores [$ delimitador] ** Lo usé en ** str_getcsv ($ value, $ delimeter); ** – MJoraid

-1

También puede utilizar fgetcsv (http://php.net/manual/en/function.fgetcsv.php) pasándole un parámetro delimitador. Si la función devuelve falso, significa que el parámetro $ delimitador no era el correcto

muestra para comprobar si el delimitador es ';'

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

$ csv_delimiter = ';'; en lugar de $ csv_delimiter = ';' –

+0

mi mal! acaba de editarlo – Rommy

+0

Lamentablemente esto no funcionó para mí – antongorodezkiy

3

Sólo estoy contando las apariciones de los diferentes delimitadores en el archivo CSV, el que tiene la mayor parte probablemente debería ser el delimitador correcto:

//The delimiters array to look through 
$delimiters = array(
    '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]; 
20

Es demasiado tarde para responder a esta pregunta, pero Espero que ayude a alguien.

Aquí hay una función simple que devolverá un delimitador de un archivo.

function getFileDelimiter($file, $checkLines = 2){ 
     $file = new SplFileObject($file); 
     $delimiters = array(
      ',', 
      '\t', 
      ';', 
      '|', 
      ':' 
     ); 
     $results = array(); 
     $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 = array_keys($results, max($results)); 
     return $results[0]; 
    } 

Utilice esta función como se muestra a continuación: preg_split

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

PS He utilizado() en lugar de explotar() debido a explotar ('\ t', $ valor) no dará adecuada resultados.

ACTUALIZACIÓN: Gracias por @RichardEB señalando un error en el código. He actualizado esto ahora.

+0

$ line = .... debe reemplazarse por: if (! $ Line = $ file-> fgets()) {break;} If $ CheckLines excede el recuento de líneas del archivo csv y habrá un error. –

+0

No hay problema, excepto que su corrección todavía arroja una excepción 'No se puede leer del archivo' cuando $ checkLines excede la cantidad de líneas de csv. Si desea compilar el cheque en el ciclo while, debe ser: while ($ file-> valid() && $ i <$ checkLines) –

+0

'Works for me' no es un buen argumento contra la lógica booleana incorrecta (p. Ej. , su configuración de manejo de errores puede ser diferente). La expresión while utiliza O no Y, y por lo tanto, se ignorará el 'final del archivo' cuando $ i esté por debajo de $ checkLines. Por ejemplo, en el archivo CSV de 1 fila, $ i se le permitirá contar hasta tres filas independientemente de que se haya alcanzado el EOF, simplemente porque (2 <= 2) = TRUE –

-2

Esta es mi solución. Funciona si sabe cuántas columnas espera. Por último, el carácter separador es el $ 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 ! */} 
} 
0

Gracias por todas sus entradas, hice mío el uso de sus trucos: preg_split, fgetcsv, bucle, etc.

Pero he implementado algo que sorprendentemente no era ¡Aquí, el uso de fgets en lugar de leer todo el archivo, mucho mejor si el archivo es pesado!

Aquí está el código:

ini_set("auto_detect_line_endings", true); 
function guessCsvDelimiter($filePath, $limitLines = 5) { 
    if (!is_readable($filePath) || !is_file($filePath)) { 
     return false; 
    } 

    $delimiters = array(
     'tab'  => "\t", 
     'comma'  => ",", 
     'semicolon' => ";" 
    ); 

    $fp = fopen($filePath, 'r', false); 
    $lineResults = array(
     'tab'  => array(), 
     'comma'  => array(), 
     'semicolon' => array() 
    ); 

    $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] = array_sum($entry)/count($entry); 
    } 

    arsort($lineResults); 
    reset($lineResults); 
    return ($lineResults[0] !== $lineResults[1]) ? $delimiters[key($lineResults)] : $delimiters['comma']; 
} 
-1

¿Qué tal algo simple?

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

Esto no funciona porque getCsvControl() solo devuelve lo que se ha establecido manualmente utilizando la función setCsvControl(). No hace ninguna conjetura mágica. – ethan

-2

Si tiene un ejemplo de archivo muy grande en GB, encabece las primeras líneas, coloque un archivo temporal. Abrir el archivo temporal en vi

head test.txt > te1 
vi te1 
+0

¡Esto no es lo que pidió OP! – CinCout

1

Solía ​​solución de @Jay Bhatt para averiguar delimitador de un archivo csv, pero no funcionó para mí, así que he aplicado algunas correcciones y comentarios para que el proceso sea más comprensible.

Ver mi versión de la función de @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); 

    // array of predefined delimiters. Add any more delimiters if you wish 
    $delimiters = array(',', '\t', ';', '|', ':'); 

    // store all the occurences of each delimiter in an associative array 
    $number_of_delimiter_occurences = array(); 

    $results = array(); 

    $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 array 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 array (comapring only the array values) 
    // in our case, the array keys are the delimiters 
    $results = array_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 recognised 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]; 
} 

yo personalmente utilizar esta función para ayudar a analizar un archivo de forma automática con PHPExcel, y funciona muy bien y rápido.

Recomiendo analizar al menos 10 líneas, para que los resultados sean más precisos.Personalmente lo uso con 100 líneas, y está funcionando rápido, sin demoras ni retrasos. Cuantas más líneas analizas, más preciso es el resultado.

NOTA: Esta es solo una versión modificada de la solución de @Jay Bhatt a la pregunta. Todos los créditos van a @Jay Bhatt.

Cuestiones relacionadas