как узнать, являются ли поля файла csv табуляцией или разделителями с запятой. Для этого мне нужна проверка php. Может ли кто-нибудь помочь? Заранее спасибо.
Слишком поздно ответить на этот вопрос, но надеюсь, что это поможет кому-то.
Вот простая функция, которая вернет разделитель файла.
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]; }
Используйте эту функцию, как показано ниже:
$delimiter = getFileDelimiter('abc.csv'); //Check 2 lines to determine the delimiter $delimiter = getFileDelimiter('abc.csv', 5); //Check 5 lines to determine the delimiter
PS Я использовал preg_split () вместо explode (), потому что explode ('\ t', $ value) не даст правильных результатов.
UPDATE: Спасибо за @RichardEB, указав на ошибку в коде. Я обновил это сейчас.
Вот что я делаю.
Это не будет работать в 100% случаев, но это достойная отправная точка. Как минимум, это уменьшит количество возможных разделителей (облегчит пользователям выбор правильного разделителя).
/* 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];
Существует 100% надежный способ определить это. Что вы можете сделать, это
В моей ситуации пользователи поставляют файлы csv, которые затем вводятся в базу данных SQL. Они могут сохранять электронную таблицу Excel как файлы с разделителями-запятыми или табуляторами. Программа, преобразующая таблицу в SQL, должна автоматически определять, будут ли поля разделены на вкладку или запятыми
Многие экспорт Excel csv имеют заголовки полей в качестве первой строки. Тест заголовка вряд ли будет содержать запятые, кроме как разделитель. Для моей ситуации я подсчитал запятые и вкладки первой строки и использовал это с большим числом, чтобы определить, является ли это csv или вкладкой
Я просто подсчитываю вхождения различных разделителей в CSV-файл, тот, который, скорее всего, должен быть правильным разделителем:
//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];
Я использовал решение @Jay Bhatt для определения разделителя csv-файла, но для меня это не сработало, поэтому я применил несколько исправлений и комментариев, чтобы этот процесс был более понятным.
См. Мою версию функции @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]; }
Я лично использую эту функцию для автоматического анализа файла с помощью PHPExcel , и он работает красиво и быстро.
Я рекомендую разбор не менее 10 строк, чтобы результаты были более точными. Я лично использую его со 100 линиями, и он работает быстро, без задержек или задержек. Чем больше строк вы разбираете, тем точнее результат.
ПРИМЕЧАНИЕ. Это всего лишь модифицированная версия решения @Jay Bhatt на вопрос. Все кредиты принадлежат @Jay Bhatt.
Помимо тривиального ответа, что c sv-файлы всегда разделены запятой – это имя, я не думаю, что вы можете придумать какие-либо жесткие правила. Файлы TSV и CSV достаточно слабо определены, что вы можете найти файлы, которые были бы приемлемыми.
A\tB,C 1,2\t3
(Предполагая \ t == TAB)
Как бы вы определили, является ли это TSV или CSV?
Когда я вывожу TSV-файл, я создаю вкладки, используя \ t тот же метод, что и автору разрыва строки, например \ n, так что, как я уже сказал, метод может быть следующим:
<?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 } ?>
Я предполагаю, что это может быть неправильным, потому что у вас также могут быть вкладки и запятые в фактическом содержимом. Это просто идея. Использование регулярных выражений может быть лучше, хотя я не слишком об этом думаю.
Спасибо за все ваши входы, я сделал мои, используя ваши трюки: preg_split, fgetcsv, loop и т. Д.
Но я реализовал то, что было удивительно не здесь, использование fgets вместо чтения всего файла, лучше, если файл тяжелый!
Вот код:
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']; }
Вы также можете использовать fgetcsv ( http://php.net/manual/en/function.fgetcsv.php ), передавая ему параметр разделителя. Если функция возвращает false, это означает, что параметр $ delimiter не был правильным
образец, чтобы проверить, является ли разделитель «;»
if (($data = fgetcsv($your_csv_handler, 1000, ';')) !== false) { $csv_delimiter = ';'; }
Как насчет чего-то простого?
function findDelimiter($filePath, $limitLines = 5){ $file = new SplFileObject($filePath); $delims = $file->getCsvControl(); return $delims[0]; }
Это мое решение. Его работы, если вы знаете, сколько столбцов вы ожидаете. Наконец, разделительный символ – это $ 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 ! */} }
Если у вас очень большой пример файла в GB, запустите первую строку, поставьте временный файл. Открыть временный файл в vi
head test.txt > te1 vi te1
Самый простой способ ответить на это – открыть его в текстовом редакторе или в TextMate.