C# Tips | 文字列処理:改行コード統一

C# C#
スポンサーリンク

はじめに 「改行コード統一」は“見えない差異でバグらないための下ごしらえ”

業務でテキストを扱っていると、
「見た目は同じなのに、なぜか比較で不一致になる」「CSVがうまく分割できない」
みたいな、地味だけどイヤなトラブルが起きます。

原因のひとつが 改行コードの違い です。

Windows、Linux、古いMac、外部システム、ブラウザ…
それぞれが違う改行コードを使っているせいで、
同じ「改行」に見えても、実際には別の文字列になってしまいます。

だからこそ、「まず改行コードを統一してから処理する」ユーティリティを持っておくと、
後続の処理がとても安定します。

ここでは、初心者向けに、

改行コードの種類と違い
なぜ統一が必要になるのか
C#での「改行コード統一ユーティリティ」の作り方
実務での使いどころと注意点

を、例題付きでかみ砕いて説明していきます。


改行コードの種類をざっくり理解する

代表的な3種類の改行コード

改行コードには、主に次の3種類があります。

CRLF(キャリッジリターン+ラインフィード)
CR(キャリッジリターン)
LF(ラインフィード)

文字として書くとこうなります。

CRLF\r\n
CR\r
LF\n

それぞれ、歴史的な事情で使われている環境が違います。

Windows:CRLF\r\n
Unix / Linux / macOS(現行):LF\n
古いMac(OS 9以前など):CR\r

今はほとんど CRLFLF の2種類を意識しておけば十分ですが、
外部システムや古いデータを扱うときには CR 単体も紛れ込むことがあります。

「見た目は同じ1行」でも中身は違う

例えば、次の2つの文字列は、画面上は同じように見えます。

"ABC\r\nDEF"
"ABC\nDEF"

でも、バイト列としては違うし、Length も違います。

"ABC\r\nDEF" の長さは 7(A B C \r \n D E F
"ABC\nDEF" の長さは 6(A B C \n D E F

この違いが、比較・分割・置換などで地味に効いてきます。


なぜ改行コードを統一する必要があるのか

比較・検索・置換が安定しなくなる

例えば、外部システムから受け取ったテキストと、
自分のシステム内で生成したテキストを比較するとします。

片方が CRLF、もう片方が LF だと、
見た目は同じでも == 比較では不一致になります。

また、「\n で Split して行ごとに処理しよう」と思っても、
中に \r\n が混ざっていると、行末に \r が残ってしまったりします。

string text = "A\r\nB\r\n";
string[] lines = text.Split('\n');

// lines[0] == "A\r"
// lines[1] == "B\r"
// 最後に空行が入ることもある
C#

この「行末に謎の \r が残る」状態は、
トリムし忘れると後続処理でバグの原因になります。

ファイルフォーマットや外部連携の仕様を守るため

CSVや固定長ファイル、外部APIの仕様で、
「改行コードはLFで」とか「Windows形式(CRLF)で」と決まっていることがあります。

このとき、内部ではどんな改行コードでも受け入れつつ、
出力時に指定の改行コードに統一する
、という設計にしておくと安全です。


C#での基本方針:「まずLFに統一」→「必要なら出力時に変換」

内部表現をLF(\n)に統一するのがおすすめ

アプリケーション内部では、
改行コードを LF(\n)に統一して扱う のがおすすめです。

理由はシンプルで、

Environment.NewLine\r\n の環境でも、\n だけで扱ったほうがシンプル
Unix系(Linux / macOS)はもともと \n
多くのライブラリやツールが「LF前提」で動いている

からです。

方針としては、

入力時:CRLFCR を全部 LF に変換
内部処理:LF 前提で Split / Replace などを行う
出力時:必要に応じて LFCRLF などに変換

という3段階に分けると、頭がスッキリします。


改行コード統一ユーティリティ(入力側:LFにそろえる)

すべての改行を \n に統一するメソッド

まずは、「どんな改行が来ても \n に統一する」ユーティリティを作ります。

public static class NewLineNormalizer
{
    public static string ToLf(string? text)
    {
        if (string.IsNullOrEmpty(text))
        {
            return string.Empty;
        }

        string normalized = text.Replace("\r\n", "\n");
        normalized = normalized.Replace("\r", "\n");

        return normalized;
    }
}
C#

ポイントは順番です。

先に \r\n\n に変換
そのあとで残った単独の \r\n に変換

こうしないと、"\r\n""\n\n" になってしまいます。

動作例

string s1 = "A\r\nB\r\nC"; // Windows形式
string s2 = "A\nB\nC";    // Unix形式
string s3 = "A\rB\rC";    // 古いMac形式

Console.WriteLine(NewLineNormalizer.ToLf(s1)); // "A\nB\nC"
Console.WriteLine(NewLineNormalizer.ToLf(s2)); // "A\nB\nC"
Console.WriteLine(NewLineNormalizer.ToLf(s3)); // "A\nB\nC"
C#

これで、「どこから来たテキストでも、とりあえずLFにそろえる」ことができます。


改行コード統一ユーティリティ(出力側:任意の形式に変換)

LFからCRLF(Windows形式)に変換する

内部でLFに統一しておけば、
出力時に「環境や仕様に合わせた改行コード」に変換するのは簡単です。

例えば、「LF → CRLF」に変換するメソッドはこう書けます。

public static class NewLineFormatter
{
    public static string ToCrLf(string? text)
    {
        if (string.IsNullOrEmpty(text))
        {
            return string.Empty;
        }

        string normalized = NewLineNormalizer.ToLf(text);

        return normalized.Replace("\n", "\r\n");
    }

    public static string ToEnvironmentNewLine(string? text)
    {
        if (string.IsNullOrEmpty(text))
        {
            return string.Empty;
        }

        string normalized = NewLineNormalizer.ToLf(text);

        return normalized.Replace("\n", Environment.NewLine);
    }
}
C#

ToCrLf は「Windows形式に固定したいとき」、
ToEnvironmentNewLine は「実行環境の標準改行に合わせたいとき」に使えます。

動作例

string internalText = "A\nB\nC"; // 内部ではLFで管理

Console.WriteLine(NewLineFormatter.ToCrLf(internalText));
// "A\r\nB\r\nC"

Console.WriteLine(NewLineFormatter.ToEnvironmentNewLine(internalText));
// Windowsなら "A\r\nB\r\nC"
// Linuxなら "A\nB\nC"
C#

これで、「内部はLF、外に出すときだけ変換」というスタイルが実現できます。


実務での使いどころ

外部から受け取ったテキストの前処理として

外部API、ファイルアップロード、メール本文、
どこから来たか分からないテキストを扱うときは、
まず最初に NewLineNormalizer.ToLf を通しておくと安心です。

string raw = ReadFromExternalSource();
string normalized = NewLineNormalizer.ToLf(raw);

// 以降は normalized を前提に処理
string[] lines = normalized.Split('\n');
C#

こうしておけば、「行末に謎の \r が残る」問題を避けられます。

ファイル出力・レポート出力の最後に

CSVやレポートをファイルとして出力するとき、
仕様書に「改行コード:CRLF」と書かれていることがあります。

その場合は、最後に ToCrLf を通してから書き出します。

string contentLf = BuildReportText(); // 内部はLF前提で組み立て
string contentCrLf = NewLineFormatter.ToCrLf(contentLf);

File.WriteAllText(path, contentCrLf, Encoding.UTF8);
C#

こうしておくと、
「開発環境はLinuxだけど、納品物はWindows形式で」というケースにも柔軟に対応できます。


ちょっとした注意点

文字列長やインデックスが変わることを意識する

改行コードを変換すると、文字列の長さが変わります。

"\r\n"(2文字) → "\n"(1文字)
"\n"(1文字) → "\r\n"(2文字)

もし、「何文字目にカーソルを置く」といった位置情報を扱っている場合は、
改行コード変換の前後でインデックスがずれることに注意が必要です。

多くの場合は、「位置情報は変換後の文字列で計算する」ようにすれば問題ありません。

すでにLF統一されている前提のライブラリもある

一部のライブラリやツールは、
「改行はLF前提」として動いていることがあります。

その場合は、入力前に ToLf を通しておくと、
ライブラリ側の想定とズレずに済みます。


まとめ 「改行コード統一ユーティリティ」は“見えない差異を消して、テキスト処理を安定させる下地作り”

改行コードの違いは、目に見えないのにバグの原因になる、
ちょっと厄介な存在です。

だからこそ、

入力時に CRLF / CR / LF をすべて LF に統一する
内部処理は LF 前提で行う
出力時にだけ、仕様や環境に合わせて LFCRLFEnvironment.NewLine に変換する
この流れをユーティリティとして固定しておく

という設計にしておくと、
「改行コードのせいでハマる」ことがぐっと減ります。

ここまで理解できれば、「なんとなく \r\n\n が混ざっている」状態から一歩進んで、
“改行コードを意識的にコントロールできるC#の文字列ユーティリティ”を、自分の手で設計・実装できるようになっていきます。

タイトルとURLをコピーしました