改行制御

教科書 p.85 の下半分には print() に対して end="" と指定することで記号の出力の途中で改行「しない」、といった説明があります。なんのことか分からないと思われますので、いくらか説明します。

この種のコンピュータの内部表現などに関する長い説明は見たくない、という人は「改行文字を使った改行の出力」までスキップするか、何だったら今は放っておいて、そんな気分になった時にまた見てください。

テキストデータ中の「行」の扱い

コンピュータ世界において「テキスト」データといえば、文字だけで構成されるデータのことを言います。しかしテキストエディタは文字の並びだけでなく「行」も扱えています。それを実現している、テキストデータの内部表現について説明します。

例えば、以下のような内容のテキストデータを作ったとします。

All work and no play

テキストエディタはテキストデータを編集する(切ったり貼ったりする)ためのものですから、以下にエディタで文字列を打ち込んだ状態を出しておきます。

image-20231208190858422

このとき、この文字データはコンピュータの内部では以下のような形で並んでいます。今タイピングの途中で、赤矢印のところでカーソルが止まっていると思ってください。

image-20231208191912611

つまりテキストデータの実体とは、メモリ中に一列に並んだ文字のデータなのです。上の図では空白一文字とデータをまだ書き込んでいないメモリ位置を区別するために、空白文字は小さな△を置いておきました。

さてここであなたは以下のように改行して、2行めから続きを打ち込んだとします。

image-20231208192145496

このとき、テキストデータはこうなっています。

image-20231208193547657

つまり「見た目は2行」のテキストになっていますが、データの内部表現としては「行」などというデータ構造はなく、ただ文字データが並ぶだけです。「改行」を意味する「特殊な文字」(上の図中では「↩︎」という記号を置いています)を打ち込むことで改行を表現しています。

つまり改行文字は、テキストデータが「文字」の並びだけではなく「行」を扱えるようにするためのものです。テキストデータにおいて、テキストはその1行の末尾に改行文字を持つわけです。だとすると、上の「見た目2行」のテキストの後半の行の末尾にも改行があるべきです。

image-20231208194028102

これで、

All work and no play

makes Jack a dull boy.

という「2行のテキストデータ」が完成しました。

ちょっと直感的でないのが VS Code エディタなどで最後の行の末尾に改行を打ち込んだときで、このとき以下のように「3行のテキストデータ」のように表示されてしまいます。

image-20231208193749018

上の表示では「 3 」などと行番号が見えていますが、そこに 3 行目はありません。これで「2行」のテキストデータだと考えてください。これを嫌って最終行の末尾の改行を取ってしまう人もいるかもしれませんが、それは避けましょう。

例えば上の二行のテキストをファイルに Allwork.txt と名前をつけて保存し、行数などを数えるコマンド、wc に -l オプションをつけて実行すると 2 と答えることが確認できます。

yasuda@Lily Documents % wc -l Allwork.txt 
       2 Allwork.txt
yasuda@Lily Documents % 

この「最後の行の改行」の扱いについては VS Code の操作 でも説明しましたね。

改行文字を使った改行の出力

こうした、目にみえるわけではないが改行文字のように表示上の(あるいは通信などまた別の処理における何らかの)制御を行う文字を「制御文字」と呼びます。この改行文字を意識して、Python の print() 文での改行処理を見てみましょう。

yasuda@Slack 08 % cat Lincoln.py    
print("government of the people,")
print("by the people,")
print("for the people.")
yasuda@Slack 08 % 

yasuda@Slack 08 % python3 Lincoln.py
government of the people,
by the people,
for the people.
yasuda@Slack 08 % 

コードには print(“by the people,”) などと書かれているだけで、特に改行文字などはつけていません。しかし出力された画面上では一つの print() 文ごとに改行されています。何故でしょうね。。。

教科書 p.84 のコードで登場し、p.85 で(ひどくシレッと)説明されているのはこの「 print() 文が出力の最後に自動的に改行文字を出している」という事実なのです。

つまり print() に end= というオプションをつけて文字(列)を渡すと、print() はその文字を出力の最後に付加します。何も指定がないと、その機能のデフォルトである「改行文字」を最後に付加します。

p.84 のコードに入っている以下の記述は、

    print("*", end="")

「“*” を出せ、そして改行の代わりに空文字を付けて出力せよ」、すなわち「“*” を出せ、そのあと改行するな」という指示です。ちょっと分かりにくいですね、、、

end="" をつけると次の行がつながる

上のプログラムを、そのようにして改行しない記述に変更して実行してみます。違いを見ると動きがわかると思います。

yasuda@Slack 08 % cat Lincoln2.py 
print("government of the people,", end="")
print("by the people,", end="")
print("for the people.")
yasuda@Slack 08 % 

yasuda@Slack 08 % python3 Lincoln2.py 
government of the people,by the people,for the people.
yasuda@Slack 08 % 

1, 2 行目の print() 文の出力から改行がなくなったため、3 つの print() 文による出力なのに、1行にまとまっています。

最後の行からも改行を取る

ところで勢いに乗って 3 行めの改行も無くしてしまうと、こんなことになります。

yasuda@Slack 08 % cat Lincoln3.py       
print("government of the people,", end="")
print("by the people,", end="")
print("for the people.", end="") <<< 最後の行でも改行しないようにした
yasuda@Slack 08 % 

yasuda@Slack 08 % python3 Lincoln3.py 
government of the people,by the people,for the people.%   <<<  この最後の "%" は白黒反転文字
yasuda@Slack 08 % 

最後の “people.” の後に改行がないですよ、ということを警告するために、zsh が白黒反転した % を出しています。スクリーンショットでないと白黒反転した状態が伝わらなさそうなので、以下につけておきます。

image-20231208205357017

このプログラムが出力するテキストはこの行が最後なのですから、そこには改行制御が必要です。このようなことにならないように。

最後に少しきれいに

ところでよく見ると「the people,by the」などとなっていて、カンマの後ろに空白がないのが気に入りません。もともと行を分けて出すためのコード記述に無理矢理 end= を入れて接続したのですから当然ですが。そこでやはり end= の制御でこれを解決してみます。

yasuda@Slack 08 % cat Lincoln4.py 
print("government of the people,", end=" ")
print("by the people,", end=" ")
print("for the people.")
yasuda@Slack 08 % 

yasuda@Slack 08 % python3 Lincoln4.py 
government of the people, by the people, for the people.
yasuda@Slack 08 % 

納得できる結果になりました。このあたりの振る舞いを見ると end= が何をしているのか、p.84 のコードや p.85 下半分、最下部のコラムの説明記述の意味がわかるのではないでしょうか。