2010-08-03 72 views

回答

8

有沒有100%可靠的方法來確定這一點。你可以做的是

  • 如果你有一個方法來驗證你讀的字段,嘗試閱讀幾個字段使用任何分隔符和驗證您的方法。如果中斷,請使用另一個。
  • 計算文件中製表符或逗號的出現次數。通常,其中一個明顯高於另一個
  • 最後但並非最不重要的是:詢問用戶,並允許他覆蓋您的猜測。
0

除了從小事回答Ç SV文件始終用逗號分隔的 - 它在這個名字,我不認爲你可以拿出任何硬性規則。 TSV和CSV文件都有足夠寬鬆的規定,您可以創建可接受的文件。

A\tB,C 
1,2\t3 

(假設\ t == TAB)

你如何將決定這是否是TSV或CSV?

+1

c可以代表'字符',這使得,;或\ t所有有效的選項。 – julesj 2012-07-02 15:52:53

+0

@julesj快速搜索建議很少使用c來表示字符,最具意義的逗號。雖然即使這樣,分隔符並不總是一個逗號,只是爲了使事情複雜化。 – 2012-07-02 19:41:17

0

當我輸出一個TSV文件I使用\ 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 
} 
?> 

我猜猜這可能不是正確的方式,因爲在實際內容中也可以有製表符和逗號。這只是一個想法。使用正則表達式可能會更好,儘管我對此不太瞭解。

-3

我回答這個問題最簡單的方法是在純文本編輯器或TextMate中打開它。

+0

這根本不適用於所問的問題。 – Jacta 2014-06-04 09:21:34

3

在我的情況下,用戶提供的csv文件被輸入到SQL數據庫中。他們可以將Excel電子表格保存爲逗號或製表符分隔的文件。將電子表格轉換爲SQL的程序需要自動識別字段是製表符分隔還是逗號

許多Excel csv導出都將字段標題作爲第一行。標題測試不可能包含逗號,除非作爲分隔符。對於我的情況,我計算了第一行的逗號和製表符,並用較大的數字來確定它是csv還是製表符

10

這就是我所做的。

  1. 解析所述第一5線CSV文件
  2. 計數中的每一行
  3. 比較每行分隔符的數量的分隔符[逗號,製表符,分號和冒號]的數量。如果你有一個格式正確的CSV,那麼每一行的分隔符數量都會匹配。

這不會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]; 
+1

很好地工作。在解析我的文件之前,我將它用作函數。它的返回值,** $ delimiters [$ delimiter] **我在** str_getcsv($ value,$ delimeter)中使用它; ** – MJoraid 2015-01-05 06:58:41

-1

您還可以使用fgetcsv(http://php.net/manual/en/function.fgetcsv.php)傳遞一個分隔符參數。如果該函數返回false,則意味着$ delimiter參數不是正確的一個樣本,用於檢查分隔符是否爲';',而不是正確的

樣本。

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

$ csv_delimiter =';';而不是$ csv_delimiter =';' – 2014-03-28 17:40:08

+0

我的不好!只是編輯它 – Rommy 2014-04-03 07:37:42

+0

不幸的是,這並沒有爲我工作 – antongorodezkiy 2015-08-03 23:57:28

3

我只是計數的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]; 
20

太晚來回答這個問題,但希望它能幫助別人。

這是一個簡單的函數,它將返回文件的分隔符。

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()而不是爆炸(),因爲爆炸( '\ t',$值)將不給予適當的結果。

更新:感謝@RichardEB指出代碼中的錯誤。我現在更新了這個。

+0

$ line = ....應替換爲:if(!$ line = $ file-> fgets()){break;} 如果$ checkLines超過csv文件的行數,那麼會出現錯誤。 – 2015-12-08 10:27:27

+0

沒問題,除非您的修補程序在$ checkLines超過csv行數時仍會引發'無法讀取文件'異常。如果你想在while循環中構建檢查,它應該是:while($ file-> valid()&& $ i <$ checkLines) – 2015-12-09 12:32:35

+0

'Works for me'不適用於錯誤的布爾邏輯(例如,你的錯誤處理配置可能不同)。你的while表達式使用OR不是AND,因此當$ i低於$ checkLines時,'文件結束'將被忽略。例如,在1行的CSV文件中,無論是否到達EOF,$ i都將被允許計數到三行,僅僅因爲(2 <= 2)= TRUE – 2015-12-10 10:07:39

-2

這是我的解決方案。 它的作品,如果你知道你期望多少列。 最後,分隔符是$ 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

感謝您的投入,我做了我的使用技巧:使preg_split,fgetcsv,循環等

但我實現了東西,是令人驚訝的不是在這裏,使用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']; 
} 
-1

怎麼樣簡單的東西?

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

這不起作用,因爲getCsvControl()只返回使用setCsvControl()函數手動設置的內容。它沒有做任何神奇的猜測。 – ethan 2015-05-05 18:29:14

-2

如果在GB中有一個非常大的文件示例,請將頭幾行放在一個臨時文件中。在vi打開臨時文件

head test.txt > te1 
vi te1 
+0

這不是OP要求的! – CinCout 2015-03-06 07:06:38

1

我用@Jay布哈特的解決方案,找出一個CSV文件的分隔符,但它並沒有爲我工作,所以我申請了幾個修復和徵求意見過程中要更多可以理解的。

見我的版本@Jay布哈特的功能:

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。