Help us understand the problem. What is going on with this article?

[php]改行コードを統一する

複数の改行コードが混ざって出力されてしまったファイルの改行コードを統一して出力し直すサンプルコードです。

環境

  • OS: Amazon Linux2
  • PHP: 7.4.14

プログラム本体

今回は、複数の改行コードが混ざって出力されたtsvファイルの改行コードをLFに統一することに加え、CSVファイルとして出力する、という内容になっています。

tsvToCsv.php
<?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ファイルの出力プログラム。

tsvOutput.php
<?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);

終わりに

改行コードとか、正規表現とか難しい・・。

参考

プログラム本体

結果確認

オマケ

C_HERO
新人時代、プログラミングの成績がワースト2%の超アナログ系。 日々の学びを記事にしていきます。
https://github.com/CHI-3
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away