これを行う方法はありますか?次のような名前のリストであるファイルがあるとします。
- アルフレッド
- 明細書
- ドナルド
x行目(この場合は3行目)に3番目の名前「Charlie」を挿入して、他のすべての名前を1行に自動的に送信するにはどうすればよいですか?私はこのような他の質問を見ましたが、彼らは役に立つ答えを得ませんでした。できればメソッドまたはループのいずれかを使用して実行できますか?
これを行う方法はありますか?次のような名前のリストであるファイルがあるとします。
x行目(この場合は3行目)に3番目の名前「Charlie」を挿入して、他のすべての名前を1行に自動的に送信するにはどうすればよいですか?私はこのような他の質問を見ましたが、彼らは役に立つ答えを得ませんでした。できればメソッドまたはループのいずれかを使用して実行できますか?
これはトリックを行う方法です。
with open("path_to_file", "r") as f:
contents = f.readlines()
contents.insert(index, value)
with open("path_to_file", "w") as f:
contents = "".join(contents)
f.write(contents)
index
およびvalue
は、選択した行と値であり、0から始まる行です。
ファイルで部分文字列を検索し、次の行に新しいテキストを追加する場合、それを行うための洗練された方法の1つは次のとおりです。
import fileinput
for line in fileinput.FileInput(file_path,inplace=1):
if "TEXT_TO_SEARCH" in line:
line=line.replace(line,line+"NEW_TEXT")
print line,
この問題を解決するのに役立つと思ったテクニックの組み合わせがあります。
with open(file, 'r+') as fd:
contents = fd.readlines()
contents.insert(index, new_string) # new_string should end in a newline
fd.seek(0) # readlines consumes the iterator, so we need to start over
fd.writelines(contents) # No need to truncate as we are increasing filesize
特定のアプリケーションでは、特定の文字列の後に追加したいと思いました。
with open(file, 'r+') as fd:
contents = fd.readlines()
if match_string in contents[-1]: # Handle last line to prevent IndexError
contents.append(insert_string)
else:
for index, line in enumerate(contents):
if match_string in line and insert_string not in contents[index + 1]:
contents.insert(index + 1, insert_string)
break
fd.seek(0)
fd.writelines(contents)
一致のすべてのインスタンスの後に文字列を挿入する場合は、最初のインスタンスだけでなく、else:
(および適切にインデントを解除する)とを削除しますbreak
。
また、は、のand insert_string not in contents[index + 1]:
後に複数のコピーを追加できないようにするmatch_string
ため、繰り返し実行しても安全です。
データをリストに読み込んで、必要な場所に新しいレコードを挿入するだけです。
names = []
with open('names.txt', 'r+') as fd:
for line in fd:
names.append(line.split(' ')[-1].strip())
names.insert(2, "Charlie") # element 2 will be 3. in your list
fd.seek(0)
fd.truncate()
for i in xrange(len(names)):
fd.write("%d. %s\n" %(i + 1, names[i]))
受け入れられた答えは、ファイル全体をメモリにロードする必要がありますが、これは大きなファイルではうまく機能しません。次のソリューションは、新しいデータが正しい行に挿入されたファイルの内容を同じディレクトリ(同じファイルシステム上)の一時ファイルに書き込み、一度にソースファイルから小さなチャンクのみを読み取ります。次に、ソースファイルを一時ファイルの内容で効率的に上書きします(Python3.8以降)。
from pathlib import Path
from shutil import copyfile
from tempfile import NamedTemporaryFile
sourcefile = Path("/path/to/source").resolve()
insert_lineno = 152 # The line to insert the new data into.
insert_data = "..." # Some string to insert.
with sourcefile.open(mode="r") as source:
destination = NamedTemporaryFile(mode="w", dir=str(sourcefile.parent))
lineno = 1
while lineno < insert_lineno:
destination.file.write(source.readline())
lineno += 1
# Insert the new data.
destination.file.write(insert_data)
# Write the rest in chunks.
while True:
data = source.read(1024)
if not data:
break
destination.file.write(data)
# Finish writing data.
destination.flush()
# Overwrite the original file's contents with that of the temporary file.
# This uses a memory-optimised copy operation starting from Python 3.8.
copyfile(destination.name, str(sourcefile))
# Delete the temporary file.
destination.close()
編集2020-09-08:コードレビューで上記と同様のことをより多くの説明で行う答えを見つけました-それは一部の人にとって役立つかもしれません。
file.readlines()
またはを使用してファイルをPythonリストに解析しますfile.read().split('\n')
list.insert()
ます。出力がどのように見えるかを示していないので、考えられる解釈の1つは、これを出力として使用することです。
(Charlieを挿入してから、後続のすべての行に1を追加します。)考えられる解決策の1つは次のとおりです。
def insert_line(input_stream, pos, new_name, output_stream):
inserted = False
for line in input_stream:
number, name = parse_line(line)
if number == pos:
print >> output_stream, format_line(number, new_name)
inserted = True
print >> output_stream, format_line(number if not inserted else (number + 1), name)
def parse_line(line):
number_str, name = line.strip().split()
return (get_number(number_str), name)
def get_number(number_str):
return int(number_str.split('.')[0])
def format_line(number, name):
return add_dot(number) + ' ' + name
def add_dot(number):
return str(number) + '.'
input_stream = open('input.txt', 'r')
output_stream = open('output.txt', 'w')
insert_line(input_stream, 3, 'Charlie', output_stream)
input_stream.close()
output_stream.close()
単純ですが効率的ではない方法は、コンテンツ全体を読み取り、変更してから書き直すことです。
line_index = 3
lines = None
with open('file.txt', 'r') as file_handler:
lines = file_handler.readlines()
lines.insert(line_index, 'Charlie')
with open('file.txt', 'w') as file_handler:
file_handler.writelines(lines)
location_of_line = 0
with open(filename, 'r') as file_you_want_to_read:
#readlines in file and put in a list
contents = file_you_want_to_read.readlines()
#find location of what line you want to insert after
for index, line in enumerate(contents):
if line.startswith('whatever you are looking for')
location_of_line = index
#now you have a list of every line in that file
context.insert(location_of_line, "whatever you want to append to middle of file")
with open(filename, 'w') as file_to_write_to:
file_to_write_to.writelines(contents)
このようにして、ファイルの途中に挿入したいデータを取得することになりました。
何が起こっているのかを明確に理解するのに苦労していたので、これは単なる擬似コードです。
基本的に、ファイル全体を読み込んでリストに追加し、そのリストに必要な行を挿入してから、同じファイルに再書き込みします。
私はこれを行うためのより良い方法があると確信しています、効率的ではないかもしれませんが、少なくとも私にはもっと理にかなっています、私はそれが他の誰かに理にかなっていることを願っています。
以下は、元のファイルを自分で作成していて、挿入場所を知っているという特殊なケースの少し厄介な解決策です(たとえば、3行目の前に追加の名前の行を挿入する必要があることを事前に知っています。ただし、残りの名前をフェッチして書き込むまで、名前はわかりません)。他の回答で説明されているように、ファイルの内容全体を読み取り、保存してから再書き込みすることは、このオプションよりも洗練されていると思いますが、大きなファイルには望ましくない場合があります。
挿入位置に非表示のヌル文字('\ 0')のバッファーを残して、後で上書きすることができます。
num_names = 1_000_000 # Enough data to make storing in a list unideal
max_len = 20 # The maximum allowed length of the inserted line
line_to_insert = 2 # The third line is at index 2 (0-based indexing)
with open(filename, 'w+') as file:
for i in range(line_to_insert):
name = get_name(i) # Returns 'Alfred' for i = 0, etc.
file.write(F'{i + 1}. {name}\n')
insert_position = file.tell() # Position to jump back to for insertion
file.write('\0' * max_len + '\n') # Buffer will show up as a blank line
for i in range(line_to_insert, num_names):
name = get_name(i)
file.write(F'{i + 2}. {name}\n') # Line numbering now bumped up by 1.
# Later, once you have the name to insert...
with open(filename, 'r+') as file: # Must use 'r+' to write to middle of file
file.seek(insert_position) # Move stream to the insertion line
name = get_bonus_name() # This lucky winner jumps up to 3rd place
new_line = F'{line_to_insert + 1}. {name}'
file.write(new_line[:max_len]) # Slice so you don't overwrite next line
残念ながら、その後に続くすべてを書き直さない限り、上書きされなかった余分なヌル文字(または一般にファイルの途中の文字)を置き換えずに削除する方法はありません。ただし、ヌル文字はファイルが人間にどのように見えるかには影響しません(幅はゼロです)。