テキストの設定ファイルなどで、最後の行を改行コードで終わらすべきか、改行コード無しで終わらすべきかで迷ったことはありませんか?

仕事で使っているアプリケーションの中には、読み込むテキストデータの最後の行に改行を入れてはダメなものがいくつもありました。

その作りはどうなのかと思いますが、このアプリケーション、動作の結果から推測すると最後の改行コードの後、空文字列のデータ行があるように処理をしているように見えました。

Pythonでプログラムを作っているとき、ふと「Pythonで行を読み込んだときはどうなる?」か気になりましたので試してみました。

改行コードで終わっているテキストと終わっていないテキストを読み込む

実際に改行コードで終わっているテキストと終わっていないテキストをPythonで読み込んでみましょう。

最初に読み込むテキストを用意します。str1は改行コード(LF)で終わっている文字列、str2は終わっていない文字列です。これをファイルに書き込みます。

>>> str1 = 'First\nSecond\n'
>>> str2 = 'First\nSecond'
>>> open('nl_included', 'wt', newline='').write(str1)
13
>>> open('nl_included', 'wt', newline='').write(str2)
12

これをテキストエディタで見ると次のようになります。カーソルがあるところがファイルの末尾です。

最後に改行コードが無いファイルをエディタで表示 最後に改行コードが有るファイルをエディタで表示

さて、これを読み込むとどうなるか見てみましょう。

まずは、改行コードで終わっているファイルです。Pythonに改行コードをいじってほしくないので、open関数に「newline=''」を指定しています。

>>> f = open('nl_included', newline='')
>>> included = f.readlines()
>>> included
['First\n', 'Second\n']

readlines()は1行ずつ読み出して、1行分の文字列を要素に持つリストを返します。返されたリストを見てみると、2行分の要素があります。

期待していた通りです。最後の改行コードの後に余計なものは付いていません。

改行コードで終わっていないファイルも読み込んでみましょう。

>>> f = open('nl_not_included', newline='')
>>> not_included = f.readlines()
>>> not_included
['First\n', 'Second']

こちらも予想通りですね。最後の行は改行コードがついていません。

末尾の改行コードを削除する

行末の改行コードを削除するにはrstrip()を使うといいでしょう。

>>> [i.rstrip('\n') for i in included]
['First', 'Second']
>>> [i.rstrip('\n') for i in not_included]
['First', 'Second']

ただし、この記事の例では読み込むときに「newline=''」を指定しているので、改行コードがLFファイルは上記のように末尾の'\n'を削除すれば良いですが、改行コードがCRLFのときには行末に'\r\n'が付くのでうまく行きません。

一般的にはopen関数のnewline引数は省略(newline引数のデフォルトはNone)するか、または明示的に「newline=None」を指定するのが良いでしょう。

まとめ

これらの結果から、Pythonでテキストを読み込む場合は何も懸念する必要はありませね。

タイトルの問いに対しては、Pythonではどちらの場合でもうまく処理できますので、こだわる必要はありませんが、ファイルへ書き出すプログラムでは、「すべての行を同じように出力する」のが良いでしょう。つまりすべての行に改行コードを付けると言うことです。

遠い昔なのでうる覚えですが、「プログラミング言語C」を読んだときに、「最後を改行コードで終わらせるのは出力するプログラムの責任」というようことが書いてあったと記憶しています。

そしてwcコマンドの骨子のCプログラム例があって、このプログラムは最後が改行コードで終わってないと、正しく行数をカウントできなかったと思います。

$ wc -l nl_included
       2 nl_included
$ wc -l nl_not_included
       1 nl_not_included

おっと、本物のwcコマンドは大丈夫かと思いましたが、Macのwcコマンドでも改行コードで終わってないと行数としてカウントしませんね。

やっぱり最後は改行コードで終わりましょう。