PHP 中處理 CSV data 的方法:我的 CSV Parser

討論一般程式設計/系統開發/網頁設計等。

版主: admin, peterlee

PHP 中處理 CSV data 的方法:我的 CSV Parser

文章peterlee » 2010-03-10, 11:58

以往我以為 PHP 已有處理 CSV 資料/檔案的方法,但原來 PHP 內置的方法如 fgetcsv() 是有問題的!
可看看:
http://www.php.net/manual/en/function.fgetcsv.php

以下則是正確的 CSV 檔案格式及相關資料(RFC 4180)。
http://tools.ietf.org/html/rfc4180

於是我就用 PHP 寫了我人生中第一個 Parser 。它基本上完全符合 RFC 4180 所述的格式。唯一不同的是, TEXTDATA 改為只要不是 COMMA 、 CR 、 DQUOTE 及 LF 等的 character 就可以。這種改變使檔案可以用非 ASCII 的 character set ,例如正體中文的 CP950 。這個 CSV Parser 主要 function csv_parse_file() 要輸入 CSV 格式的 string $in 。輸入格式正確的話, return value 就是 TRUE ,否則是 FALSE 。 CSV Parser 得出的結果是一個 2d array $out 。 $out[m][n] 就是第 (m + 1) row 第 (n + 1) column 的資料。
以下是我的 CSV Parser 整套的 PHP code :
代碼: 選擇全部
function csv_parse_file($in, &$out){
   $out = array();
   $n = (int) strlen($in);
   $p0 = (int) 0;
   $p1 = (int) 0;
   $p2 = (int) 0;
   $header = array();
   $record = array();
   if (csv_parse_header($in, $p0, $p1) && csv_parse_CRLF($in, $p1, $p2)){
      csv_parse_header($in, $p0, $p1, TRUE, $header);
      $out[] = $header;
      csv_parse_CRLF($in, $p1, $p2, TRUE);
      $p0 = (int) $p2;
   }
   if (csv_parse_record($in, $p0, $p1)){
      csv_parse_record($in, $p0, $p1, TRUE, $record);
      $out[] = $record;
      $p0 = (int) $p1;
   } else {
      return FALSE;
   }
   while (TRUE){
      if (csv_parse_CRLF($in, $p0, $p1) && csv_parse_record($in, $p1, $p2)){
         csv_parse_CRLF($in, $p0, $p1, TRUE);
         csv_parse_record($in, $p1, $p2, TRUE, $record);
         $out[] = $record;
         $p0 = (int) $p2;
      } else {
         break;
      }
   }
   if (csv_parse_CRLF($in, $p0, $p1)){
      csv_parse_CRLF($in, $p0, $p1, TRUE);
      $p0 = (int) $p1;
   }
   if ($p0 == $n){
      return TRUE;
   } else {
      return FALSE;
   }
}
function csv_parse_header($in, $s, &$e, $accept = FALSE, &$out = NULL){
   $out = array();
   $n = (int) strlen($in);
   if ($s >= $n){
      return FALSE;
   }
   $p0 = (int) $s;
   $p1 = (int) $s;
   $p2 = (int) $s;
   $name = (string) '';
   if (csv_parse_name($in, $p0, $p1)){
      if ($accept){
         csv_parse_name($in, $p0, $p1, TRUE, $name);
         $out[] = (string) $name;
      }
      $p0 = (int) $p1;
   } else {
      return FALSE;
   }
   while (TRUE){
      if (csv_parse_COMMA($in, $p0, $p1) && csv_parse_name($in, $p1, $p2)){
         if ($accept){
            csv_parse_COMMA($in, $p0, $p1, TRUE);
            csv_parse_name($in, $p1, $p2, TRUE, $name);
            $out[] = (string) $name;
         }
         $p0 = (int) $p2;
      } else {
         break;
      }
   }
   $e = (int) $p0;
   return TRUE;
}
function csv_parse_record($in, $s, &$e, $accept = FALSE, &$out = NULL){
   $out = array();
   $n = (int) strlen($in);
   if ($s >= $n){
      return FALSE;
   }
   $p0 = (int) $s;
   $p1 = (int) $s;
   $p2 = (int) $s;
   $field = (string) '';
   if (csv_parse_field($in, $p0, $p1)){
      if ($accept){
         csv_parse_field($in, $p0, $p1, TRUE, $field);
         $out[] = (string) $field;
      }
      $p0 = (int) $p1;
   } else {
      return FALSE;
   }
   while (TRUE){
      if (csv_parse_COMMA($in, $p0, $p1) && csv_parse_field($in, $p1, $p2)){
         if ($accept){
            csv_parse_COMMA($in, $p0, $p1, TRUE);
            csv_parse_field($in, $p1, $p2, TRUE, $field);
            $out[] = (string) $field;
         }
         $p0 = (int) $p2;
      } else {
         break;
      }
   }
   $e = (int) $p0;
   return TRUE;
}
function csv_parse_name($in, $s, &$e, $accept = FALSE, &$out = NULL){
   $out = (string) '';
   $n = (int) strlen($in);
   if ($s >= $n){
      return FALSE;
   }
   $p0 = (int) $s;
   $p1 = (int) $s;
   $field = (string) '';
   if (csv_parse_field($in, $p0, $p1)){
      if ($accept){
         csv_parse_field($in, $p0, $p1, TRUE, $field);
         $out = (string) $field;
      }
      $p0 = (int) $p1;
   } else {
      return FALSE;
   }
   $e = (int) $p0;
   return TRUE;
}
function csv_parse_field($in, $s, &$e, $accept = FALSE, &$out = NULL){
   $out = (string) '';
   $n = (int) strlen($in);
   if ($s >= $n){
      return FALSE;
   }
   $p0 = (int) $s;
   $p1 = (int) $s;
   $escaped = (string) '';
   $nonescaped = (string) '';
   if (csv_parse_escaped($in, $p0, $p1)){
      if ($accept){
         csv_parse_escaped($in, $p0, $p1, TRUE, $escaped);
         $out = (string) $escaped;
      }
      $p0 = (int) $p1;
   } else if (csv_parse_nonescaped($in, $p0, $p1)){
      if ($accept){
         csv_parse_nonescaped($in, $p0, $p1, TRUE, $nonescaped);
         $out = (string) $nonescaped;
      }
      $p0 = (int) $p1;
   } else {
      if ($accept){
         $out = (string) '';
      }
   }
   $e = (int) $p0;
   return TRUE;
}
function csv_parse_escaped($in, $s, &$e, $accept = FALSE, &$out = NULL){
   $out = (string) '';
   $n = (int) strlen($in);
   if ($s >= $n){
      return FALSE;
   }
   $p0 = (int) $s;
   $p1 = (int) $s;
   $p2 = (int) $s;
   $t = (string) '';
   if (csv_parse_DQUOTE($in, $p0, $p1)){
      if ($accept){
         csv_parse_DQUOTE($in, $p0, $p1, TRUE);
      }
      $p0 = (int) $p1;
   } else {
      return FALSE;
   }
   while (TRUE){
      if (csv_parse_TEXTDATA($in, $p0, $p1)){
         if ($accept){
            csv_parse_TEXTDATA($in, $p0, $p1, TRUE, $t);
            $out .= (string) $t;
         }
         $p0 = (int) $p1;
      } else if (csv_parse_COMMA($in, $p0, $p1)){
         if ($accept){
            csv_parse_COMMA($in, $p0, $p1, TRUE, $t);
            $out .= (string) $t;
         }
         $p0 = (int) $p1;
      } else if (csv_parse_CR($in, $p0, $p1)){
         if ($accept){
            csv_parse_CR($in, $p0, $p1, TRUE, $t);
            $out .= (string) $t;
         }
         $p0 = (int) $p1;
      } else if (csv_parse_LF($in, $p0, $p1)){
         if ($accept){
            csv_parse_LF($in, $p0, $p1, TRUE, $t);
            $out .= (string) $t;
         }
         $p0 = (int) $p1;
      } else if (csv_parse_DQUOTE($in, $p0, $p1) && csv_parse_DQUOTE($in, $p1, $p2)){
         if ($accept){
            csv_parse_DQUOTE($in, $p0, $p1, TRUE, $t);
            csv_parse_DQUOTE($in, $p1, $p2, TRUE, $t);
            $out .= (string) $t;
         }
         $p0 = (int) $p2;
      } else {
         break;
      }
   }
   if (csv_parse_DQUOTE($in, $p0, $p1)){
      if ($accept){
         csv_parse_DQUOTE($in, $p0, $p1, TRUE);
      }
      $p0 = (int) $p1;
   } else {
      return FALSE;
   }
   $e = (int) $p0;
   return TRUE;
}
function csv_parse_nonescaped($in, $s, &$e, $accept = FALSE, &$out = NULL){
   $out = (string) '';
   $n = (int) strlen($in);
   if ($s >= $n){
      return FALSE;
   }
   $p0 = (int) $s;
   $p1 = (int) $s;
   $t = (string) '';
   while (TRUE){
      if (csv_parse_TEXTDATA($in, $p0, $p1)){
         if ($accept){
            csv_parse_TEXTDATA($in, $p0, $p1, TRUE, $t);
            $out .= (string) $t;
         }
         $p0 = (int) $p1;
      } else {
         break;
      }
   }
   if ($p0 == $s){
      return FALSE;
   }
   $e = (int) $p0;
   return TRUE;
}
function csv_parse_COMMA($in, $s, &$e, $accept = FALSE, &$out = NULL){
   $out = (string) '';
   $n = (int) strlen($in);
   if ($s >= $n){
      return FALSE;
   }
   if ($in[$s]==','){
      if ($accept){
         $out = (string) ',';
      }
      $e = (int) ($s + 1);
      return TRUE;
   } else {
      return FALSE;
   }
}
function csv_parse_CR($in, $s, &$e, $accept = FALSE, &$out = NULL){
   $out = (string) '';
   $n = (int) strlen($in);
   if ($s >= $n){
      return FALSE;
   }
   if ($in[$s]=="\r"){
      if ($accept){
         $out = (string) "\r";
      }
      $e = (int) ($s + 1);
      return TRUE;
   } else {
      return FALSE;
   }
}
function csv_parse_DQUOTE($in, $s, &$e, $accept = FALSE, &$out = NULL){
   $out = (string) '';
   $n = (int) strlen($in);
   if ($s >= $n){
      return FALSE;
   }
   if ($in[$s]=='"'){
      if ($accept){
         $out = (string) '"';
      }
      $e = (int) ($s + 1);
      return TRUE;
   } else {
      return FALSE;
   }
}
function csv_parse_LF($in, $s, &$e, $accept = FALSE, &$out = NULL){
   $out = (string) '';
   $n = (int) strlen($in);
   if ($s >= $n){
      return FALSE;
   }
   if ($in[$s]=="\n"){
      if ($accept){
         $out = (string) "\n";
      }
      $e = (int) ($s + 1);
      return TRUE;
   } else {
      return FALSE;
   }
}
function csv_parse_CRLF($in, $s, &$e, $accept = FALSE, &$out = NULL){
   $out = (string) '';
   $n = (int) strlen($in);
   if ($s >= $n){
      return FALSE;
   }
   $p0 = (int) $s;
   $p1 = (int) $s;
   $p2 = (int) $s;
   $t = (string) '';
   if (csv_parse_CR($in, $p0, $p1) && csv_parse_LF($in, $p1, $p2)){
      if ($accept){
         csv_parse_CR($in, $p0, $p1, TRUE, $t);
         $out .= (string) $t;
         csv_parse_LF($in, $p1, $p2, TRUE, $t);
         $out .= (string) $t;
      }
      $p0 = (int) $p2;
   } else {
      return FALSE;
   }
   $e = (int) $p0;
   return TRUE;
}
function csv_parse_TEXTDATA($in, $s, &$e, $accept = FALSE, &$out = NULL){
   $out = (string) '';
   $n = (int) strlen($in);
   if ($s >= $n){
      return FALSE;
   }
   $p0 = (int) $s;
   $p1 = (int) $s;
   if (csv_parse_COMMA($in, $p0, $p1)){
      return FALSE;
   }
   if (csv_parse_CR($in, $p0, $p1)){
      return FALSE;
   }
   if (csv_parse_DQUOTE($in, $p0, $p1)){
      return FALSE;
   }
   if (csv_parse_LF($in, $p0, $p1)){
      return FALSE;
   }
   if ($accept){
      $out = (string) $in[$s];
   }
   $e = (int) ($s + 1);
   return TRUE;
}
peterlee
第 6 級閒人
 
文章: 720
註冊時間: 2007-12-10, 09:15
來自: Hong Kong

回到 程式設計/系統開發/網頁設計

誰在線上

正在瀏覽這個版面的使用者:沒有註冊會員 和 1 位訪客



閒人會 - 網頁排行榜