質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

1005閲覧

Python mode='r+'でopen時のwriteの書込位置の変化

Ruda0316

総合スコア1

Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/08/31 07:50

前提・実現したいこと

 Pythonの勉強中です。mode='r+'でopenした場合の、read/write位置について疑問に思い、試していたところ、おかしな現象に遭遇しました。
mode='r+'でopen直後にwriteした後、何もしないかreadするとデータが先頭から上書きされるのに、readlineすると末尾に追加で書き込まます。後の命令により書込位置が変わるのは、Pythonのような言語では良くない現象と思います。
これは、Pythonの言語仕様なのでしょうか? それともバグ?

発生している問題・エラーメッセージ

以下に実行結果を示します。

original *********************************** '0123456789\nabcdefghij\n' write : '987654321' write *********************************** '9876543219\nabcdefghij\n' write,read *********************************** read: '9\nabcdefghij\n' '9876543219\nabcdefghij\n' write,readline *********************************** readline: '0123456789\n' '0123456789\nabcdefghij\n987654321'

該当のソースコード

Python

1s_org = '0123456789\nabcdefghij\n' 2s_write = '987654321' 3 4print('original ***********************************') 5with open('test.txt', mode = 'w') as f: 6 f.write(s_org) 7with open('test.txt') as f: 8 s_read = f.read() 9print(repr(s_read)) 10print('write :',repr(s_write)) 11 12print('\nwrite ***********************************') 13with open('test.txt', mode = 'r+') as f: 14 f.write(s_write) 15with open('test.txt') as f: 16 s_read = f.read() 17 print(repr(s_read)) 18 19print('\nwrite,read ***********************************') 20with open('test.txt', mode = 'w') as f: 21 f.write(s_org) 22with open('test.txt', mode = 'r+') as f: 23 f.write(s_write) 24 s_read1 = f.read() 25print('read:', repr(s_read1)) 26with open('test.txt') as f: 27 s_read = f.read() 28 print(repr(s_read)) 29 30print('\nwrite,readline ***********************************') 31with open('test.txt', mode = 'w') as f: 32 f.write(s_org) 33with open('test.txt', mode = 'r+') as f: 34 f.write(s_write) 35 s_line1 = f.readline() 36print('readline:', repr(s_line1)) 37with open('test.txt') as f: 38 s_read = f.read() 39 print(repr(s_read))

試したこと

ネットで検索してみましたが、同様な事象に関する情報は見つけられませんでした。

補足情報(FW/ツールのバージョンなど)

試した環境は、以下の通り。
Windows10 Home x64 21H1 ビルド 19043.1165 Python 3.9.5 x64
Windows 11 Pro ビルド 22000.168 Python 3.9.6 x64。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

テキストモードでなく、バイナリーモードだと期待通りの動作ですね。

書き込み処理を遅延すること自体は良いのですが、テキストモードの場合は、書き込み専用に独立したバッファに書き込んでいると思われ、読み込みと同期が取れていません。
これはバグじゃないのかなぁ。
write()の後、flush()だけでなく、tell()seek(0,0)というファイルに何も影響を与えないはずのメソッドを行っても書き込みバッファと読み込みバッファの同期が取れるようです。readwriteが混在する場合は切り替わるタイミングで処理系内部で同期を取るべきでしょうね。

なお、遅延された書き込みが1行目の次でなくファイル末尾に書かれていますが、テキストモードの場合は、ストリーム位置が先頭でない場合には末尾に書き込まれるようです。

投稿2021/08/31 09:17

otn

総合スコア85901

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Ruda0316

2021/09/01 07:32

回答ありがとうございます。 やはり、バグだと思いますよね。 ただ、この様な利用方法はあまりされないと思うし、flush()という明確な回避策もある現状、諦めてそういう仕様と思って使うようにしようと思います。自分の感覚的には、まぁ、実際には使う事はほとんど無いと思いますが。
otn

2021/09/01 10:45

readとwriteを交互に繰り返すようなケースでは、毎回flush()するとディスク書き込みが頻繁に発生するので、そういうケースではtell()が良いかと思います。
otn

2021/09/01 11:58

とりあえずバグレポート出してみました。
Ruda0316

2021/09/01 12:17

今のバージョンのPythonであれば、tell()の方が良さそうなのは確かですね。でも、次以降のバージョンでは、同期出来る保証は無いと思います。元々、Pythonは実効速度を求める言語では無いですし、OSとかのキャッシュ機能に期待する手もあるかと。ですので、言語仕様に素直に従い、flush()で行こうと思います。
otn

2021/09/01 12:45

flushも意味はディスクに書き込むという意味なので、この現象がバグであることを前提にすると、読み書きを繰り返す際に、読み取りバッファと書き込みバッファの整合性を取るのに常に有効かというのは、やってみないと分からないです。 Python2.6だとこの現象は出ないですね。
guest

0

書き込みバッファが実際のファイルIOに遅延して反映されているようです。ややこしいですね。
実際、このように変更すると同期的に書き込みと読み込みが行われます。

python

1with open('test.txt', mode = 'r+') as f: 2 f.write(s_write) 3 f.flush() 4 s_line1 = f.readline()

投稿2021/08/31 08:29

mather

総合スコア6759

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Ruda0316

2021/08/31 08:39

回答ありがとうございます。 flush()があるということは、Pythonにおいては、非同期入出力のタイミング制御は、ユーザーの責任と言うことでしょうか? その場合、入力データが出力バッファの反映が間に合わず、タイミングにより変化することはしかたがないと思います。しかし、すでに出力バッファいにあるデータの書き込み位置が、後の処理により変化するのは問題だと思うのですが、いかがでしょうか?
mather

2021/08/31 08:57

バッファ処理のタイミングについては特に指示がなければシステムに依存すると思いますが、正しい動作を確約させたい場合はユーザーの責任において処理を記述する必要があります。 > すでに出力バッファいにあるデータの書き込み位置が、後の処理により変化するのは問題だと思うのですが、いかがでしょうか? 私個人の意見では、ややわかりにくいとはいえ、上記のようにコントロールする方法があるのですから問題には感じません。 理解が難しいと感じるのであれば、 "r+"のモードを使わないようにすれば良いでしょう。
Ruda0316

2021/09/01 07:22

'r+'だけでなく、'+'付きのテキストモード共通の問題のように思えます。ただ、まぁ、普通に使うかと言われると、自分の感覚的には、あまり使いそうも無いです。どうしても使う場合は、flush()を利用するようにします。 それでも、write()は、書込位置を特定せず出力バッファにデータを格納するだけで、実際の出力位置は出力されるときに決定されるなどという仕様は、Pythonのような高級言語にふさわしくないと思います。
guest

0

ファイルポインタ、でぐぐって出てくる解説を読んでみよう

投稿2021/08/31 07:55

y_waiwai

総合スコア88042

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Ruda0316

2021/08/31 09:19

回答ありがとうございます。 tellを使って、入出力の前後のポインタを確認しようとしたのですが、うまくいきませんでした。タイミングが変わってしまっためか、readlineした場合のwrite動作が、他の場合と同じに変わってしまいました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問