複数の改行コードが混ざって出力されてしまったファイルの改行コードを統一して出力し直すサンプルコードです。
環境
- OS: Amazon Linux2
- PHP: 7.4.14
プログラム本体
今回は、複数の改行コードが混ざって出力されたtsvファイルの改行コードをLFに統一することに加え、CSVファイルとして出力する、という内容になっています。
<?php
$tsvfile = 'data.tsv';
$csvfile = 'data.csv';
if(file_exists('data.tsv')){
// 読み込みモードでファイルを開く
$handle = fopen($tsvfile, 'r');
$contents = fread($handle, filesize($tsvfile));
// 改行コードがCRLFもしくはCRの場合に、LFに置換
// $str = preg_replace('/\t/', ',', preg_replace(['/\r\n/', '/\r/'], "\n", $contents));
$str = preg_replace('/\t/', ',', preg_replace('/\r\n?/', "\n", $contents));
// 書き出しモードでファイルを開く(複数回実施の場合は上書きになる)
$fp = fopen($csvfile, 'w');
fwrite($fp, $str);
fclose($fp);
}else{
echo 'tsvfile is not existed!';
}
ポイント
(1) preg_replace
正規表現検索および置換を行います。
置換元の文字列が複数種類存在する場合は、第1引数に、配列として格納します。
注意点として、配列の前のほうから検索を行うため、CRLF(\r\n)よりも、CR(\r)を前に置いてしまうと、CRLFのCRの部分のみ置換されてしまいます。
必ずCRLF(\r\n)を配列の前の方に格納するようにしましょう。
@il9437 さんのご指摘をもとに、置換元を文字列での記載に変更しました。
「?」を使うことで、0回もしくは1回マッチしたものを対象とすることができます。
結果確認
元ファイルの改行コード
まず、元ファイルであるdata.tsvの内容を確認します。
catコマンドに-Aオプションをつけることで、不可視文字(改行コード、タブ、スペースなど)を確認することができます。
$ cat -A data.tsv
aaa^IAaa^IAAa^IAAA$
bbb^IBbb^IBBb^IBBB^Mccc^ICcc^ICCc^ICCC^M$
登場する不可視文字は以下の通り。
表示 | 意味 | 備考 |
---|---|---|
^I | タブ | - |
$ | LF | 主にUnix系OS全般、Mac OS Xで使用される改行コード |
^M$ | CRLF | 主にWindows系OSで使用される改行コード |
^M | CR | 主に古いMac OS(9以前)で使用されていた改行コード |
出力ファイルの改行コード
続いて、出力されたdata.csvの改行コードも確認しておきましょう。
$ cat -A data.csv
aaa,Aaa,AAa,AAA$
bbb,Bbb,BBb,BBB$
ccc,Ccc,CCc,CCC$
不可視文字は「$(LF)」のみになっていますね。うまくいきました!
※TSV→CSVとして出力しているため、「^I(タブ)」だった箇所はカンマ「,」になっています。
オマケ
以下、元となるTSVファイルの出力プログラム。
<?php
$tsvfile = 'data.tsv';
$array = [
['aaa', 'Aaa', 'AAa', 'AAA' . "\n"],
['bbb', 'Bbb', 'BBb', 'BBB' . "\r"],
['ccc', 'Ccc', 'CCc', 'CCC' . "\r\n"]
];
// 追記モードでファイルを開く
$fp = fopen($tsvfile, 'a');
foreach($array as $vals){
for($i = 0; $i < count($vals); $i++){
if($i < count($vals) - 1){
fwrite($fp, $vals[$i]."\t");
}else{
fwrite($fp, $vals[$i]);
}
}
}
fclose($fp);
終わりに
改行コードとか、正規表現とか難しい・・。