別のパソコンで開いたテキストファイルが、行末にずらりと「^M」を並べていた。
そんな見慣れない記号に、私は一瞬ぎょっとしました。
中身は何も壊れていないのに、ファイルだけが別人のような顔をしている。
GitHub の差分が、一文字も書き換えていないはずの行を真っ赤に染めることもあります。
犯人は、ふだん目に見えない改行コードという小さな制御文字でした。
この一文字、あるいは二文字の違いが、なぜこれほど大きな波紋を広げるのでしょうか。
改行コードの正体は、二つの制御文字
行を変えるという操作を、コンピュータは一つの記号では持っていません。
画面の上では何も見えないのに、データの中には「ここで改行する」という命令がちゃんと埋め込まれている。
その命令の正体が、CR と LF という二種類の制御文字。
0x0D と 0x0A という、目に見えない住人
CR は十六進数で 0x0D、十進数なら 13 という値を持つ制御文字を指します。
LF のほうは 0x0A、十進数で 10 にあたる別の文字。
プログラムの世界では、CR を \r、LF を \n という記号で書き分けます。
文章を書く私たちは改行を一つの動作だと感じていますが、機械の内側では二つの異なる番地に住む別々の文字として扱われている。
タイプライターが残した「キャリッジ」と「紙送り」
なぜ改行が二つの文字に分かれているのか、答えはタイプライターの時代にさかのぼります。
紙に文字を打つあの機械では、行を変えるのに二つの動作が必要でした。
一つは、印字するヘッド(キャリッジ)を行の左端へ戻す動き。
これが Carriage Return、すなわち CR の語源になります。
もう一つは、紙を一行分だけ上へ送り出す動き。
こちらが Line Feed、LF の由来です。
横に戻して、縦に送る。
二つの動作がそろって初めて、次の行の先頭からまた文字を打てるようになったのです。
物理的なこの手順がそのまま文字コードの世界へ持ち込まれたと考えると、CRLF という二文字の並びが急に腑に落ちてくる。
なぜ OS ごとに改行コードが分かれたのか
CR と LF という二つの部品がそろうと、組み合わせ方に「方言」が生まれました。
同じ改行という目的に対して、システムごとに違う流儀が選ばれていく。
Windows の CRLF、Unix の LF、旧 Mac の CR
Windows 系は、タイプライターの作法を律儀に受け継いで CR と LF を並べたCRLFを採用しています。
Unix や Linux、そして現在の macOS は、LF だけで一行の終わりを表す。
かつての Mac OS(バージョン 9 以前)は、逆に CR ひとつを改行として使っていました。
つまり世の中には、同じ「改行」をめぐるCRLF・LF・CR という三つの流派が併存している。
HTTP やメール(SMTP)といった通信の約束ごとでは、いまも CRLF が正式な行の区切りとして決められています(公式)。
歴史の枝分かれが、そのまま今日のファイルの中に化石のように残っている。
一文字の違いが生む、よくある落とし穴
この方言の差は、ふだんは意識されないまま静かに眠っています。
問題が表に出るのは、異なる流派のあいだをファイルが行き来した瞬間。
たとえば Windows で書いたシェルスクリプトを Linux で動かすと、行末の CR が余分な文字として残り、コマンドが見えない一文字のせいで突然エラーを吐く。
Linux のツールがその CR を ^M という形で見せるのは、まさにこの状況です。
Git でも改行コードがチームのあいだでばらつくと、中身を変えていない行まで差分として浮かび上がってしまう。
レビューの画面が意味のない真っ赤な差分で埋まり、本当に見るべき変更が埋もれてしまうのです。
私自身、原因の見えない差分を前にして「これは何かに呪われているのでは」と独りごちたこともあります。
正体さえ分かってしまえば、なんてことのない一文字のすれ違いでした。
改行コードと上手につき合うコツ
落とし穴を知ったうえで大切なのは、改行コードを成り行き任せにしない仕組みを最初に用意しておくこと。
入口と保管場所の二段構えで、方言をあらかじめそろえてしまう発想です。
エディタと .editorconfig で入口をそろえる
まず手元のエディタで、ファイルを保存するときの改行コードを意識して選ぶ習慣を持っておきたい。
VS Code なら画面の右下に現在の改行コードが表示され、クリックひとつで CRLF と LF を切り替えられます。
チームで設定を共有したいなら、プロジェクトの根もとに .editorconfig を一枚置くのが効いてくる。
# .editorconfig(抜粋)
root = true
[*]
end_of_line = lf
charset = utf-8
# 省略
この数行があるだけで、参加メンバーのエディタが同じ改行コードで保存してくれるようになります。
人の注意力ではなく、設定ファイルのほうに約束を肩代わりさせる。
Git の .gitattributes で「正」を一つに決める
エディタの設定をすり抜けたファイルに備えて、もう一段の安全網を張っておきたいところ。
その役割を担うのが .gitattributes という設定ファイルです。
# .gitattributes(抜粋)
* text=auto eol=lf
*.sh text eol=lf
*.bat text eol=crlf
# 省略
text=auto はテキストファイルを自動で見分け、リポジトリの中では改行を LF に統一して記録してくれる(公式)。
シェルスクリプトは LF、バッチファイルは CRLF と、ファイルの種類ごとに正しい改行を指定できるのも心強いところ。
リポジトリという保管場所の改行をひとつに決めておけば、誰の環境を経由しても差分が改行コードで汚れない状態を保てます。
入口をエディタでそろえ、保管場所を Git でそろえる。
この二段構えこそが、見えない一文字に振り回されないための現実的な落としどころなのです。
最後に
改行コードは、画面に映らないからこそ厄介で、そのぶん奥行きのあるテーマでした。
CR と LF という二つの制御文字、タイプライターから受け継いだ横移動と縦送り、そして OS ごとに枝分かれした CRLF・LF・CR の三流派。
ここまで分解してみると、行末で起きるトラブルの多くが流派のすれ違いという一点に行き着くのが見えてきます。
仕組みを知り、.editorconfig と .gitattributes で入口と保管場所をそろえておけば、見えない一文字はもう手強い相手ではありません。
目に見えないものの正体を一つずつ手なずけていく、その積み重ねこそがエンジニアという仕事の静かな面白さだと、私は思っています。
以上です。








コメントを残す