PHP Tips | 文字列処理:実務向け便利系 - TSV 用エスケープ

PHP PHP
スポンサーリンク

なぜ「TSV 用エスケープ」が必要になるのか

TSV は「Tab Separated Values」、区切り文字がカンマではなくタブ(\t)のテキスト形式です。
CSV よりシンプルに見えますが、「そのまま文字列をタブでつなぐだけ」で済むのは、かなり運がいいケースだけです。

実務でよくあるのは、こういう値です。

  • 値の中にタブが紛れ込んでいる
  • 値の中に改行が入っている
  • 値の中にダブルクォートやカンマも普通に出てくる

TSV では「タブが区切り」「改行が行の区切り」なので、
この 2 つをどう扱うかをきちんと決めておかないと、読み込み側で行・列がズレます。

CSV と違って「公式な厳密仕様」があまり浸透していないぶん、
プロジェクトごとに“ローカルルール”を決めて、それに合わせたエスケープを用意するのが現実的です。


TSV の前提ルールをざっくり決める

よくある実務ルールの一例

まずは、「どういう方針でエスケープするか」を決めます。
ここでは、次のようなシンプルなルールを採用します。

  • 区切り文字はタブ \t
  • 行の区切りは \r\n(Windows 互換)
  • フィールド内にタブや改行があったら、\t\n のような「見える形」にエスケープする
  • ダブルクォートで囲む方式は使わない(TSV では必須ではないため)

つまり、「TSV は“タブ区切りのプレーンテキスト”で、危ない制御文字だけをバックスラッシュ付きで逃がす」という方針です。

この方針だと、読み込み側も「\t をタブに戻す」「\n を改行に戻す」だけで元の値を復元できます。


1 フィールド用の「TSV エスケープ関数」を作る

制御文字を「見える形」に変換する

まずは、1 セル分の文字列を安全な形に変換する関数を作ります。

やりたいことはシンプルです。

  • 実際のタブ文字 \t → 文字列としての \t
  • 実際の改行 \r / \n\r / \n
  • バックスラッシュ \ 自体もエスケープしておく(\\\
/**
 * 1 フィールド分の文字列を TSV 用にエスケープする
 *
 * 制御文字(タブ・改行・バックスラッシュ)を「見える形」に変換する。
 */
function tsv_escape_field(string $value): string
{
    // まずバックスラッシュをエスケープ
    $value = str_replace('\\', '\\\\', $value);

    // タブを \t に
    $value = str_replace("\t", '\\t', $value);

    // 改行を \n / \r に
    $value = str_replace("\r", '\\r', $value);
    $value = str_replace("\n", '\\n', $value);

    return $value;
}
PHP

使い方の例です。

echo tsv_escape_field("山田\t太郎");
// 出力: 山田\\t太郎

echo tsv_escape_field("1行目\n2行目");
// 出力: 1行目\\n2行目
PHP

ここでのポイントは、「実際のタブ・改行を、TSV の構造を壊さない“ただの文字列”に変えている」ということです。
TSV の区切りとして使うタブ・改行と、値の中に含まれるタブ・改行を、はっきり分離できます。


行全体を組み立てるユーティリティにする

配列 → 1 行分の TSV 文字列

次に、配列から 1 行分の TSV を作る関数を用意します。

/**
 * 配列から 1 行分の TSV 文字列を生成する
 */
function array_to_tsv_line(array $fields, string $delimiter = "\t"): string
{
    $escaped = [];

    foreach ($fields as $value) {
        $escaped[] = tsv_escape_field((string)$value);
    }

    // タブで結合し、行末に \r\n を付ける
    return implode($delimiter, $escaped) . "\r\n";
}
PHP

使い方の例です。

$row = [
    1,
    "山田\t太郎",
    "1行目\n2行目",
];

echo array_to_tsv_line($row);
// 例: 1\t山田\\t太郎\t1行目\\n2行目\r\n
PHP

この文字列をファイルに順番に書いていけば、
「タブ区切り・1 行ごとに \r\n・値の中の制御文字はエスケープ済み」の TSV ができます。


読み込み側のことも少しだけ意識する

エスケープを「戻す」処理のイメージ

こちらはユーティリティの範囲を少し超えますが、
エスケープした以上、読み込み側で「元に戻す」処理も必要になります。

イメージとしては、次のような逆変換です。

  • \\t → 実際のタブ \t
  • \\n → 実際の改行 \n
  • \\r → 実際の復帰 \r
  • \\\\ → バックスラッシュ \

書くとこうなります。

function tsv_unescape_field(string $value): string
{
    $value = str_replace('\\t', "\t", $value);
    $value = str_replace('\\n', "\n", $value);
    $value = str_replace('\\r', "\r", $value);
    $value = str_replace('\\\\', '\\', $value);

    return $value;
}
PHP

実務では、「出力側と入力側で同じルールを共有する」ことが何より大事です。
TSV は CSV より自由度が高いぶん、「プロジェクト内の約束事」をきちんと決めておくと、後で困りません。


まとめ:今日からの「TSV 用エスケープ」ユーティリティ

TSV 用エスケープの本質は、「タブと改行(構造を決める文字)を、値の中から追い出して“ただの文字列”に変える」ことです。そのためにやるべきことは、次の 2 点に集約されます。

  • 値の中のタブ・改行・バックスラッシュを、\t\n\r\\ のような見える形に変換する。
  • 行全体は「タブ区切り+\r\n 終端」で組み立てる。

ユーティリティとしてまず持っておくと便利なのは、この 2 本です。

function tsv_escape_field(string $value): string
{
    $value = str_replace('\\', '\\\\', $value);
    $value = str_replace("\t", '\\t', $value);
    $value = str_replace("\r", '\\r', $value);
    $value = str_replace("\n", '\\n', $value);

    return $value;
}

function array_to_tsv_line(array $fields, string $delimiter = "\t"): string
{
    $escaped = [];

    foreach ($fields as $value) {
        $escaped[] = tsv_escape_field((string)$value);
    }

    return implode($delimiter, $escaped) . "\r\n";
}
PHP

もし、あなたのコードが「implode("\t", $row) だけで TSV を作っている」なら、そこがこのユーティリティを差し込むポイントです。タブと改行を一段“見える形”に逃がしてあげるだけで、「壊れにくい TSV」に一気に近づきます。

PHPPHP
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました