人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

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

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

●質問者: worldwideyrp
●カテゴリ:コンピュータ ウェブ制作
○ 状態 :終了
└ 回答数 : 3/3件

▽最新の回答へ

1 ● foobar_777
●67ポイント

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


foobar_777さんのコメント
上で言っている \nは改行ではなく、行中に出てきている\nの意味です。

worldwideyrpさんのコメント
早速のご教示をいただきありがとうございます。 printに限らずwrite()などでもエスケープシーケンスが処理されるのは理解しております。ここで疑問なのは、ファイルの行のテキストをそのまま読み込んで >>> print "1行目\n1行目" という命令が実行されてると思っていたのに、実際は >>> print "1行目\\n1行目" という命令になっていた、という点です。内部のデータが変換されているというよりは、ファイルを読み込んだ時点ですでに \\n という形になって内部のデータとして格納されているように思えるので、それは何故だろう、というところです。 わかりづらくて申し訳ありません。

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')

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

質問者から

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

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


2 ● 井戸端さん
●67ポイント

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


3 ● TransFreeBSD
●66ポイント

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

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のときの挙動は若干自信なし。
#実際のところ特に内部表現は文字コードだけの話じゃないので不正確ではある

関連質問

●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ