改行文字の問題
テキストファイルで用いられる改行文字はOSによって微妙に異なります。
0x0a (line feed: LF)単独で改行を表すOS:LinuxなどUNIX系、バージョン10以降のmacOSなど多くのOS
0x0d 0x0a (CR LF)の2文字で改行を表すOS:Windows、かつてのMS-DOS、さらに昔のCP/M (ファイルではありませんが多くのインターネット系のプロトコルでも改行文字はCR LFです)
0x0d (carriage return: CR)単独で改行を表すOS:バージョン9以前のMac OS
これらはOSが決めたルールで、各OS上で動くアプリケーションも基本的にこのルールに従って動作します。例えばCにおいて
code:C
printf("Hello, world!\n");
と書いてあると、標準出力には'H','e','l','l','o',','…,'l','d','!'のASCIIコード=0x48, 0x65, 0x6c, 0x6c, 0x6f, …, 0x6c, 0x64, 0x21に続いて
LinuxやmacOSの場合は0x0aの1バイトが出力される(LFのみ)
Windowsの場合は0x0d, 0x0aの2バイトが出力される(CR LF)
ということになります(CRのみの場合は最近滅多に見ない)。つまりプログラム上は'\n'の1文字に見えますが、Windowsの場合だけ"\r\n"の2文字が出力されるのです。このことを確かめたければ、例えば簡単なプログラム
code:hello.c
int main(void)
{
printf("Hello, world!\n");
return 0;
}
をコンパイルし実行形式を作って、リダイレクトを使ってファイルを作ってみるとよく分かります。
Linuxの場合はこうなります
https://gyazo.com/7868ccdd207b52043577359b6a2bc286
Windowsの場合はこうなります
https://gyazo.com/a480084b3792cc01259f401acff2aea3
最近の多くのテキストエディタはこれらの改行文字の違いを自動的に判別しますが、対応していないテキストエディタ等では不具合が起きることがあります。例えばWindows 8.1のメモ帳でLinux由来のテキストファイルを開くと(改行文字が0x0d 0x0aつまりCR LFではなく単に0x0aつまりLFなので)改行文字が認識されず全ての行が1行に連なって表示されてしまいます。逆にLinuxで改行文字の自動判別ができないテキストエディタを使ってWindowsから複製したテキストファイルを開くと、全ての行末に^Mなる文字(キャレット記法と呼ばれる、表示可能な文字コードがない場合の表示法で、文字コード0x01, 0x02…から順に^A, ^B…と表記する。よって^Mは0x0dを表す)がくっついているように見えてしまいます。これはCプログラムファイルでも例外ではありません。Windowsで書いたCプログラムをLinuxにファイル転送して、それを改行文字の自動判別ができないエディタ(例えばMicroEmacs)で開くとこんなことになります。
https://gyazo.com/d4c452aff6922166b50105a089569c1a
この各行の最後の^Mがキャレット記法の特殊文字、ASCIIコードで0x0dの文字つまりcarriage return(CR)です。LFは改行として認識されているのですが、CRは余計であるためこれだけがキャレット文字として残ってしまうのです。コンパイル時にはこれは改行文字つまり空白文字として扱われますので支障はないのですが、OSが決めたルールに合わせる方が何かと面倒が減ります。
この講義やプログラミング演習1ではWindowsの人でもWindows Subsystem for Linux(WSL)を使って演習することを推奨しています。WSLはWindows内で動作するLinux環境ですので、WSL内のファイルはLinuxのルールに従うべきです。Cプログラムファイルでも改行文字はLFのみにして下さい。Visual Studio Codeの場合は右下に改行文字の種類が出ています。CRLFになっている場合は、クリックしてLFにしてから保存しましょう。 https://gyazo.com/715069187149fa6c2eefdd99aab0e581
なお最近、クラウドサービスが増えてきました。クラウドサービス上では「現在どのOSを使っているのか」が曖昧なので改行文字がどちらであるべきかという明確なルールがありません。どちらでも良い場合はクラウド上に保存するテキストファイルはLFで統一した方が良いかと思います。例えばWindows上でGitを使う場合には、多くの人はGitHubやGitLabも使うでしょうから、コミット時に改行文字をLFに変換する設定にしておいた方が安全です。その設定(autocrlf)についてはここに解説があります。 またUnicodeにはCR, LFに加えて以下のような文字を改行文字=行の終わりを表す文字として定義しています。
U+000B (UTF-8で0x0b, vertical tab: VT) 次のタブ行(6の倍数の行)までの空白
U+000C (UTF-8で0x0c, form feed: FF) 次のページまでの空白
U+0085 (UTF-8で0x85, next line: NEL) 次の行
U+2028 (UTF-8で0xe2 0x80 0xa8, line separator: LS) 行の区切り
U+2029 (UTF-8で0xe2 0x80 0xa9, paragraph separator: PS) パラグラフの区切り
しかしこれらの文字はCでは規格上改行文字として扱われていません。その代わり、これらの文字がプログラム中に入っていても空白として扱います(規格上はVTとFFだけが空白として扱われていますが、多くのコンパイラは上記の全てを空白と同等に扱うようです)。またこれらの文字を改行として扱えるエディターも今のところなさそうです。Visual Studio Codeの場合は、LSやPSがあると「普通は使われない行末記号」(Unusual Line Terminators)であるとして削除を促します。