Python にて open や codecs.open した際の \n の扱いについて


次の3行のUTF-8テキストファイル test.txt を読み込む処理をします
環境はWindows:改行CR+LFです

1行目\n1行目
2行目\n2行目
3行目\n3行目

Pythonコード:

>>> import codecs
>>> f = codecs.open("test.txt", "r", "utf-8")
>>> t = f.readline()
>>> print t

出力結果:
1行目\n1行目

\n の箇所で改行がされずそのまま文字として出力されています。
また、後半2行だけ変えてreadlines()で読み込みをします。

>>> ts = f.readlines()
>>> print ts

出力結果:
[u'1\u884c\u76ee\\n1\u884c\u76ee\r\n', u'2\u884c\u76ee\\n2\u884c\u76ee\r\n', u'3\u884c\u76ee\\n3\u884c\u76ee\r\n']

自動的に \n が \\n と変換されているように見えます
通常の open でも同様でした(行末は \r\n ではなく \n となった)

replace()にて \n → \\n という処理を自前で行ってから出力してきたので、今回のように勝手に \n という出力が出てきたことに少々戸惑っています

【質問】
・このように \n → \\n 自動変換の旨が記載されている公式ドキュメントのありかは?
・つまり総じて \ → \\ という変換が随時行われている、という理解で合っているか?
・この自動変換が起きうる組み込み関数等は他にあるか?

以上、どうぞ宜しくお願いいたします

回答の条件
  • 1人5回まで
  • 13歳以上
  • 登録:2014/04/10 22:03:55
  • 終了:2014/04/17 22:05:04

回答(3件)

id:foobar_777 No.1

foobar_777回答回数78ベストアンサー獲得回数182014/04/10 23:27:50

ポイント67pt

エスケープシーケンスという物です。
ドキュメントはたとえば以下にあります。
http://docs.python.jp/2.5/ref/strings.html (真ん中あたりの表)
1) \n -> \\n になったというよりは \(バックスラッシュ) -> \\ になっています。
2) プリントする際にこのような表示になっています。
  print 時に表示上の都合でこうなっています。
  たとえば、print ts の部分をprint ts[0] にすると print t の時と同じく\nが表示されます。
3) 内部のデータが変換されているわけではありません。

他2件のコメントを見る
id:foobar_777

ファイルtest.txt中の文中の\nはエスケープシーケンスとして扱われず
読み込み時はそのまま読み込まれています。(「\」+「n」になります。)
Pythonを質問時の状態(1行目tを読み込んだ状態)にして、以下を実行してみてください。
 (2行目はタブで始まり、3行目は改行のみです。)

>>> for i in range(len(t)):
... print t[i] + "\t" + hex(ord(t[i]))
...


質問の意図は文中の\nが改行として扱われてほしいと言うことでしょうか?
おそらく、お望みの動作をさせるには次のようにする必要があります。
print t.replace('\\n', '\n')
print ts[0].replace('\\n', '\n')
print ts[1].replace('\\n', '\n')

2014/04/11 06:56:33
id:foobar_777

コードをそもまま貼り付けたらタブが消えました。補ってください。

2014/04/11 06:57:35
id:worldwideyrp

よくよく考えてみましたら、readline()やreadlines()は「行」を単位として読み込むのが目的なわけですから、その「行」という単位の内部にさらに「改行」があったら目的に合わなくなるということになりますよね。その意味では、「行」の中に \n があったらマズくて、読み込むときに \\n という形になっていた方が「一行分を読み込む」という目的に合致するということは理解できます。

ただ、その根拠となるドキュメントが見当たりません。 http://docs.python.jp/2.5/tut/node9.html の「7.2.1 ファイルオブジェクトのメソッド」にて f.readline() の説明などもありますが、終端に \n が残るという説明にとどまっており、途中に \n があった場合にどう処理するのかに言及されておりません。
わかりづらい質問で恐縮ですが、引き続きどうぞよろしくお願いいたします。

id:watercooler No.2

井戸端さん回答回数289ベストアンサー獲得回数512014/04/11 08:40:26

ポイント67pt

Python内部はすべて¥nに統一されているの。
http://docs.python.jp/2.6/library/functions.html#open

Python が全改行文字サポートを行っている (標準ではしています) 場合、ファイルがテキストファイルで開かれますが、行末文字として Unix における慣行である '\n' 、Macintosh における慣行である '\r' 、 Windows における慣行である '\r\n' のいずれを使うこともできます。これらの改行文字の外部表現はどれも、 Python プログラムからは '\n' に見えます。


読み書きは外部環境に合わせてWindowsなら¥r¥nで区切って読み込んで内部用の¥nに変えてくれるってことね。

終端に¥nを残すのだから途中に¥nがあったらまずいわよねエスケープしましょって流れでわかると思うからいちいち説明なんてしないと思うわ。

もちろん書くときは¥nから¥r¥nに変換してくれるわよ。蛇足だったかしら?
http://python.matrix.jp/pages/tips/compatibility/line_feed.html

id:TransFreeBSD No.3

TransFreeBSD回答回数654ベストアンサー獲得回数2632014/04/11 15:54:20

ポイント66pt

ファイル内のデータ、内部表現、リテラル表現がごっちゃになってるようです。

printに限らずwrite()などでもエスケープシーケンスが処理されるのは理解しております。

違います。printやwriteはエスケープシーケンスを処理しません。
エスケープシーケンスを処理するのは「''」や「""」などを使った文字列リテラルです。
http://docs.python.jp/2/reference/lexical_analysis.html#strings

foobar_777さんの言うとおり、readやreadline, readlinesではエスケープシーケンスを処理しません。
なので「\n」(5c6e)はそのまま「\n」(5c6e)となります。
一方、改行文字(0aまたは0d0a)は井戸端さんの言うとおり、組み込みのopenでは改行文字(0a)に統一されますが、codecsでは改行文字(0aまたは0d0a)のまま(本当はunicode化されてる?)です。
http://docs.python.jp/2/library/functions.html#open
http://docs.python.jp/2/library/codecs.html#codecs.open

一方printはエスケープシーケンスは処理しませんが、対象が文字列かそれ以外かで動作が変わります。
文字列の場合はそのままです。文字列でなければ文字列変換規則を使って文字列に変換されます。
readlinesで得られるのは文字列ではなく文字列のリストオブジェクトなのでこの文字列変換規則が適用されます。
http://docs.python.jp/2/reference/simple_stmts.html#print
http://docs.python.jp/2/reference/expressions.html#string-conversions

(特に、文字列を変換すると、値を安全に出力するために文字列の両側にクオートが付けられ、”変 (funny) な” 文字はエスケープシーケンスに変換されます。)

こういう表し方をリテラル表現などと呼びます。
一方、python内部では先ほどのように「\n」は5c6e、改行文字は0aになってます。
また、ファイル内部などはutf8だったりしますが、python内部はunicodeになっています。
こういったpython内部での形態を内部表現といいます。
#なので入出力時にはutf8←→unicode変換の話もありますが省略(よく覚えてない)します。

今回の質問の場合でいうと、「1行目\n1行目」をそれぞれで書くと
ファイル内(utf8):31e8a18ce79bae5c6e31e8a18ce79bae0d0a
内部表現(unicodeベース):31 e8 a1 8c e7 9b ae 5c 6e 31 e8 a1 8c e7 9b ae 0a または 0031 884c 76ee 005c 006e 0031 884c 76ee 000d 000a
リテラル表現:5b7527315c75383834635c...(省略w)
#codecs.openじゃないopenのときの挙動は若干自信なし。
#実際のところ特に内部表現は文字コードだけの話じゃないので不正確ではある

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません