首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 讀寫 Tab 分隔檔案(TSV/CSV 變體)完整實作

     編輯:PHP 綜合知識

核心讀寫函數

<?php
/**
 * 將陣列資料寫入 Tab 分隔檔案
 * @param string $filepath 檔案路徑
 * @param array $data 二維陣列或關聯陣列
 * @param bool $saveKeys 是否寫入鍵名作為第一欄
 * @return bool 成功與否
 */
function writeTabbedFile($filepath, array $data, $saveKeys = false) {
    $content = '';
    
    foreach ($data as $key => $row) {
        // 清理欄位中的 Tab 與換行
        $cleanKey = str_replace(["\t", "\n", "\r"], ' ', $key);
        $cleanRow = array_map(fn($v) => str_replace(["\t", "\n", "\r"], ' ', $v), $row);
        
        if ($saveKeys) {
            $content .= $cleanKey . "\t";
        }
        
        $content .= is_array($cleanRow) ? implode("\t", $cleanRow) : $cleanRow;
        $content .= "\n";
    }
    
    // 原子寫入:先寫暫存檔再重命名
    $tempFile = $filepath . '.tmp';
    if (file_put_contents($tempFile, $content) === false) {
        return false;
    }
    
    return rename($tempFile, $filepath);
}

/**
 * 讀取 Tab 分隔檔案為陣列
 * @param string $filepath 檔案路徑
 * @param bool $loadKeys 第一欄是否為鍵名
 * @return array
 */
function loadTabbedFile($filepath, $loadKeys = false) {
    $data = [];
    
    if (!file_exists($filepath) || !is_readable($filepath)) {
        return $data;
    }
    
    $handle = fopen($filepath, 'r');
    if (!$handle) return $data;
    
    while (($line = fgets($handle)) !== false) {
        $line = rtrim($line, "\n\r");
        if ($line === '') continue;
        
        $fields = explode("\t", $line);
        
        if ($loadKeys) {
            $key = array_shift($fields);
            $data[$key] = $fields;
        } else {
            $data[] = $fields;
        }
    }
    
    fclose($handle);
    return $data;
}
?>

使用範例

<?php
// 測試資料
$records = [
    'user_001' => ['張三', '25', 'engineer', '台北'],
    'user_002' => ['李四', '30', 'designer', '台中'],
    'user_003' => ['王五', '28', 'manager', '高雄'],
    'config' => ['site_name' => 'MySite', 'debug' => 'false'], // 扁平資料也支援
];

// 寫入
writeTabbedFile('data.tsv', $records, true);

/* data.tsv 內容:
user_001	張三	25	engineer	台北
user_002	李四	30	designer	台中
user_003	王五	28	manager	高雄
config	site_name	debug
*/

// 讀取
$loaded = loadTabbedFile('data.tsv', true);
print_r($loaded);
/* 輸出:
Array
(
    [user_001] => Array ( [0] => 張三 [1] => 25 [2] => engineer [3] => 台北 )
    [user_002] => Array ( [0] => 李四 [1] => 30 [2] => designer [3] => 台中 )
    [user_003] => Array ( [0] => 王五 [1] => 28 [2] => manager [3] => 高雄 )
    [config] => Array ( [0] => site_name [1] => debug )
)
*/
?>

進階版:支援標題列、型別轉換、大檔案串流

<?php
class TabbedFileHandler {
    private $filepath;
    private $delimiter = "\t";
    private $enclosure = ''; // TSV 通常不包圍字符
    private $hasHeader = false;
    private $header = [];
    
    public function __construct($filepath, array $options = []) {
        $this->filepath = $filepath;
        $this->delimiter = $options['delimiter'] ?? "\t";
        $this->enclosure = $options['enclosure'] ?? '';
        $this->hasHeader = $options['has_header'] ?? false;
    }
    
    /** 串流讀取(記憶體友善) */
    public function readStream(callable $callback) {
        $handle = fopen($this->filepath, 'r');
        if (!$handle) throw new RuntimeException("無法開啟檔案:{$this->filepath}");
        
        $rowNum = 0;
        while (($row = fgetcsv($handle, 0, $this->delimiter, $this->enclosure)) !== false) {
            if ($this->hasHeader && $rowNum === 0) {
                $this->header = $row;
            } else {
                $data = $this->hasHeader ? array_combine($this->header, $row) : $row;
                $callback($data, $rowNum);
            }
            $rowNum++;
        }
        fclose($handle);
    }
    
    /** 串流寫入 */
    public function writeStream(iterable $dataRows, array $header = []) {
        $tempFile = $this->filepath . '.tmp';
        $handle = fopen($tempFile, 'w');
        if (!$handle) throw new RuntimeException("無法建立暫存檔");
        
        if ($header) {
            fputcsv($handle, $header, $this->delimiter, $this->enclosure);
        }
        
        foreach ($dataRows as $row) {
            fputcsv($handle, $row, $this->delimiter, $this->enclosure);
        }
        
        fclose($handle);
        rename($tempFile, $this->filepath);
    }
    
    /** 型別轉換器 */
    public function withTypeCasting(array $castMap) {
        return new class($this, $castMap) extends TabbedFileHandler {
            private $castMap;
            public function __construct($parent, $castMap) {
                parent::__construct($parent->filepath);
                $this->castMap = $castMap;
            }
            public function readStream(callable $callback) {
                parent::readStream(function($row, $num) use ($callback) {
                    foreach ($this->castMap as $col => $type) {
                        if (isset($row[$col])) {
                            $row[$col] = $this->castValue($row[$col], $type);
                        }
                    }
                    $callback($row, $num);
                });
            }
            private function castValue($value, $type) {
                switch ($type) {
                    case 'int': return (int)$value;
                    case 'float': return (float)$value;
                    case 'bool': return filter_var($value, FILTER_VALIDATE_BOOLEAN);
                    case 'json': return json_decode($value, true);
                    default: return $value;
                }
            }
        };
    }
}

// 使用:處理大檔案,只讀取需要的欄位並轉型
$handler = new TabbedFileHandler('big_data.tsv', ['has_header' => true]);
$handler->withTypeCasting(['age' => 'int', 'salary' => 'float', 'active' => 'bool'])
        ->readStream(function($row) {
            if ($row['age'] > 30 && $row['active']) {
                echo "高薪資深員工:{$row['name']} - {$row['salary']}\n";
            }
        });
?>

TSV vs CSV 選擇指南

特性TSV (Tab)CSV (Comma)
欄位含逗號無需轉義需雙引號包圍
欄位含 Tab需轉義/替換無問題
人類可讀性對齊較好標準試算軟體支援佳
資料庫匯入/匯出MySQL LOAD DATA 預設 TabExcel/Google Sheets 原生
建議場景程式間交換、日誌、大量數據人工編輯、報表匯出
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved