改行コードの違いを体感してみる
テキストを入力して、保存して、再び画面に入力したままを表示する。これはコンピュータを操作する上で、最も基本的な欲求である。出来て当然のことなのだけど、稀に出来なくて思い悩むことがある。
最近のGUI環境は気が利いているので、ほとんどの場合、良きに計らい正しく表示してくれる。しかし、コマンドの世界では、文字コードにまつわるすべての設定を自分でコントロールする必要がある。すると、とたんにこの最も基本的な欲求を満たせなくなることが多い。(自分のこと)
なぜ文字化けしてしまうのか?なぜ1行しか表示されないのか?なぜgrepで検索されないのか?なぜ1行ずつループ処理してくれないのか?文字コードにまつわる疑問は多い...。基本的なことを理解していれば、思い悩む必要はないのに、毎回無駄に悩んで、時間を浪費している気がする。
まずは文字コードの違いから、ちゃんと調べ直してみた。
体感する道具
- Xcodeをインストール済みであること。
- Homebrewをインストール済みであること。
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
- nkfコマンドをインストール済みであること。
$ brew install nkf
最終行しか表示されない現象
- 例えば、Finderでアプリケーションフォルダを開いて、
- プレビュー.app、マップ.app、メール.appを選択してコピー、
- テキストエディットを開いて標準テキストの新規書類にペースト、
プレビュー.app マップ.app メール.app
- ファイル名:Finder_cp.txtとして保存してみる。
- catコマンドで確認すると...
$ cat $HOME/Documents/Finder_cp.txt
メール.app
- 最終行のメール.appしか表示されない。
- プレビュー.appとマップ.appはどこへ行った?
すべての行が表示される場合
- 今度は、テキストエディットを開いて標準テキストの新規書類に、まったく同じ内容を手入力してみる。
プレビュー.app マップ.app メール.app
- ファイル名:Finder_input.txtとして保存してみた。
- catコマンドで確認すると...
$ cat $HOME/Documents/Finder_input.txt
プレビュー.app
マップ.app
メール.app
- ちゃんと3行すべてが表示された。
ダンプ表示で違いを知る
- この違いを知るには、odコマンドでダンプ(16進数コード)表示してみると、よく分かる。
$ od -tx1c $HOME/Documents/Finder_cp.txt 0000000 e3 83 97 e3 83 ac e3 83 93 e3 83 a5 e3 83 bc 2e プ ** ** レ ** ** ビ ** ** ュ ** ** ー ** ** . 0000020 61 70 70 0d e3 83 9e e3 83 83 e3 83 97 2e 61 70 a p p \r マ ** ** ッ ** ** プ ** ** . a p 0000040 70 0d e3 83 a1 e3 83 bc e3 83 ab 2e 61 70 70 p \r メ ** ** ー ** ** ル ** ** . a p p 0000057 $ od -tx1c $HOME/Documents/Finder_input.txt 0000000 e3 83 97 e3 83 ac e3 83 93 e3 83 a5 e3 83 bc 2e プ ** ** レ ** ** ビ ** ** ュ ** ** ー ** ** . 0000020 61 70 70 0a e3 83 9e e3 83 83 e3 83 97 2e 61 70 a p p \n マ ** ** ッ ** ** プ ** ** . a p 0000040 70 0a e3 83 a1 e3 83 bc e3 83 ab 2e 61 70 70 p \n メ ** ** ー ** ** ル ** ** . a p p 0000057
- オレンジ色の太字がその違い。
- ちょうど改行する部分である。
- \rは、CR(キャリッジリターン)の意味。16進数コードで0dになっている。
- 印字ヘッド(キャリッジ)を行頭に戻す(リターン)、という意味。
- \nは、LF(ラインフィード*1)の意味。16進数コードで0aになっている。
- 1行分(ライン)用紙を送る・すすめる(フィード)、という意味。
改行コードの違い
- ちなみに、Windows環境は、CR LFというコードの並びを改行コードと解釈する。
改行コード | 代表的な環境 |
---|---|
LF(0x0a) | UNIX |
CR(0x0d) | MacOS |
CRLF(0x0d0a) | Windows |
- CRを見つけたUNIX環境は、文字を出力する位置を行頭に戻す。
- ところが、改行はしないので、次の文字は同じ行に上書きされることになる。
- そのような動作が繰り返され、最終行の「メール.app」のみが見えていたのだ。
正しく改行する
- テキストエディット.appは、どのような改行コードも良きに計らい解釈してくれる。とても親切である。
- 一方、コマンドの世界では、基本的にはOS環境に依存した改行コードしか、改行と認めてくれない。
- そのため、正しく改行するためには、改行コードを変換する必要があるのだ。
$ cat $HOME/Documents/Finder_cp.txt | tr '\r' '\n'
プレビュー.app
マップ.app
メール.app
- これで正しく表示された、と喜んではいけない。
- もしWindows環境のファイルだったら、余分な空行が入ることが目に見えている。
プレビュー.app マップ.app メール.app
- CR LF → LF LF と変換されてしまうので。
- CR LFの改行コードにも対応するなら、もうちょっと工夫する必要がある。
- まず思いつくのはsedによる置き換えだが...これはNG。
$ cat $HOME/Documents/Finder_cp.txt | sed 's/\r\n|\r/\n/g'
メール.app.app
$ cat $HOME/Documents/Finder_cp.txt | perl -pe 's/\r\n|\r/\n/g'
プレビュー.app
マップ.app
メール.app
- ちなみに、rubyでも変換できる。
$ cat $HOME/Documents/Finder_cp.txt | ruby -Ku -pe 'gsub(/\r\n|\r/,"\n")'
$ cat $HOME/Documents/Finder_cp.txt | nkf -Lu
プレビュー.app
マップ.app
メール.app
- nkfなら、他の改行コードもオプション指定だけで自在に変換できるのだ。
オプション | 代表的な環境 | 改行コード |
---|---|---|
nkf -Lu | UNIX環境 | LF |
nkf -Lm | MacOS環境 | CR |
nkf -Lw | Windows環境 | CR LF |
*1:またはNL(ニューライン)と呼ばれることもある。