舊系統常以固定寬度文字檔交換資料(如銀行匯入檔、主機下載報表)。需將其轉為 CSV 才能匯入資料庫或試算表處理。
繼承 SplFileObject,利用迭代器逐行處理,記憶體效率高。支援自訂欄位位置/寬度、分隔符、換行符。
<?php
/**
* 固定寬度文字檔轉 CSV 類別
*
* @author Kevin Waterson (改寫優化)
*/
class Fixed2CSV extends SplFileObject {
private $fields = []; // [起始位置 => 寬度]
private $separator = ',';
private $eol = "\n";
public function __construct(string $filename) {
parent::__construct($filename);
}
// 魔術方法:設定屬性
public function __set(string $name, $value): void {
switch ($name) {
case 'fields':
if (!is_array($value)) throw new InvalidArgumentException('fields 必須為陣列');
$this->fields = $value;
break;
case 'separator':
$this->separator = (string)$value;
break;
case 'eol':
$this->eol = (string)$value;
break;
default:
throw new Exception("無法設定屬性: $name");
}
}
// 魔術方法:取得屬性預設值
public function __get(string $name) {
switch ($name) {
case 'fields': return [];
case 'separator': return ',';
case 'eol': return "\n";
default: throw new Exception("屬性不存在: $name");
}
}
// 覆寫 current():將固定寬度行轉為 CSV
public function current(): string|false {
$line = parent::current();
if ($line === false) return false;
$csv = '';
$fieldKeys = array_keys($this->fields);
$fieldCount = count($fieldKeys);
foreach ($fieldKeys as $i => $start) {
$width = $this->fields[$start];
$fieldValue = trim(substr($line, $start, $width));
$csv .= $fieldValue;
$csv .= ($i < $fieldCount - 1) ? $this->separator : $this->eol;
}
return $csv;
}
}
<?php
require_once 'Fixed2CSV.php';
try {
// 範例:每行 80 字元,欄位定義
// 欄位1:位置 0,寬度 10
// 欄位2:位置 10,寬度 15
// 欄位3:位置 25,寬度 20
// 欄位4:位置 45,寬度 25
$file = new Fixed2CSV('data/fixed_width.txt');
$file->fields = [
0 => 10,
10 => 15,
25 => 20,
45 => 25,
];
$file->separator = ',';
$file->eol = "\n";
echo "完整欄位輸出:\n";
foreach ($file as $line) {
echo $line;
}
// 僅輸出第 1、3 欄位
$file2 = new Fixed2CSV('data/fixed_width.txt');
$file2->fields = [
0 => 10,
25 => 20,
];
echo "\n僅第 1、3 欄位:\n";
foreach ($file2 as $line) {
echo $line;
}
} catch (Exception $e) {
echo "錯誤: " . $e->getMessage();
}
ID001 NAME001 TAIPEI 20240101
ID002 NAME002 TAICHUNG 20240102
ID003 NAME003 KAOHSIUNG 20240103
ID001,NAME001,TAIPEI,20240101
ID002,NAME002,TAICHUNG,20240102
ID003,NAME003,KAOHSIUNG,20240103
LOAD DATA INFILE 或 PDO 批次匯入$this->setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY),或使用 iconv() 轉 UTF-8current() 中依欄位索引做 intval()、floatval()、日期解析