Pythonを使用してファイル内の特定の行を削除する


145

ニックネームがいっぱいのテキストファイルがあるとします。Pythonを使用して、このファイルから特定のニックネームを削除するにはどうすればよいですか?


1
試してみてくださいfileinput@ JF-セバスチャンによって記載されているように、ここで。簡単なfor構文で、一時ファイルを介して1行ずつ作業できるようです。
ケビン

回答:


205

まず、ファイルを開いて、ファイルからすべての行を取得します。次に、ファイルを書き込みモードで再度開き、削除する行を除いて、行を書き戻します。

with open("yourfile.txt", "r") as f:
    lines = f.readlines()
with open("yourfile.txt", "w") as f:
    for line in lines:
        if line.strip("\n") != "nickname_to_delete":
            f.write(line)

strip("\n")比較で改行文字を使用する必要があるのは、ファイルが改行文字で終わっていない場合、最後の文字も終了しないためですline


2
なぜ私たちはそれを2回開閉しなければならないのですか?
Ooker 2014年

3
@Ooker:最初のモードではファイルの現在の行を読み取っているだけなので「読み取り専用」であるため、ファイルを2回開く(その間で閉じる)必要があります。次に、ファイルを閉じて、「書き込みモード」で再度開きます。ファイルは書き込み可能であり、ファイルの内容を、削除したい行に置き換えます。
Devin 14

4
なぜPythonではこれを1行で行うことができないのですか?
Ooker 14

5
@Ooker、ラインを読むとき、カーソルがラインに沿って動くのを想像してみてください。その行が読み取られると、カーソルはその行を通過します。ファイルに書き込もうとすると、現在カーソルがある場所に書き込みます。ファイルを再度開くと、カーソルがリセットされます。
Waddas 14

4
withコンパウンドを使用してください!
Sceluswe 2016

101

単一のオープンのみでこの問題を解決する:

with open("target.txt", "r+") as f:
    d = f.readlines()
    f.seek(0)
    for i in d:
        if i != "line you want to remove...":
            f.write(i)
    f.truncate()

このソリューションは、ファイルをr / wモード( "r +")で開き、シークを使用してfポインターをリセットしてから、最後の書き込み後にすべてを削除するために切り捨てます。


2
私もロックファイル(fcntl)を使わなければならなかったので、これは私にとって非常にうまくいきました。私はfcntlと一緒にfileinputを使用する方法を見つけることができませんでした。
Easyrider 2015年

1
このソリューションのいくつかの副作用を確認するとよいでしょう。
user1767754 2017年

3
私はこれをしません。forループでエラーが発生した場合、ファイルが部分的に上書きされ、重複した行または行が半分切り取られてしまいます。代わりにf.truncate()直後にしたい場合がありf.seek(0)ます。そうすれば、エラーが発生した場合、ファイルが不完全になります。しかし、実際の解決策(ディスク容量がある場合)は、一時ファイルに出力し、すべてを成功させた後で、それを使用するos.replace()pathlib.Path(temp_filename).replace(original_filename)、元のファイルと交換することです。
ボリス

i.strip('\n') != "line you want to remove..."受け入れられた回答に記載されているように追加してもらえれば、それで私の問題は完全に解決されます。理由だけでi私のために何もしませんでした
Mangohero1

31

すべてをリストに保存し、ファイルを再度開いて書き込むのではなく、最良かつ最速のオプションは、ファイルを別の場所に書き直すことです。

with open("yourfile.txt", "r") as input:
    with open("newfile.txt", "w") as output: 
        for line in input:
            if line.strip("\n") != "nickname_to_delete":
                output.write(line)

それでおしまい!1つのループと1つだけで同じことができます。それははるかに速くなります。


通常のforループを使用する代わりに、ジェネレータ式を使用できます。この方法では、プログラムがファイルからメモリにすべての行をロードしないため、大きなファイルの場合はお勧めできません。一度にメモリに1行しかありません。ジェネレーター式のforループは次のようになります(output.write(line) for line in input if line!="nickname_to_delete"+"\n")
shrishinde

4
@ShriShindeまた、ファイルオブジェクトをループするときにファイルをメモリに読み込まないため、このソリューションは提案と同じように機能します。
Steinar Lima 2016

あなたは、元のファイルを削除し、LinuxのOS上のPythonで次のようになり、元のファイル名に2番目のファイルを、名前を変更したい場合がありますsubprocess.call(['mv', 'newfile.txt', 'yourfile.txt'])
マックス

6
os.replace(python v 3.3の新機能)は、へのシステムコールよりもクロスプラットフォームですmv
7yl4r 2017年

シンプルで素晴らしい。
JuBaer AD

27

これは、@ Lotherの回答からの「フォーク」です(これは正しい回答と見なす必要があると思います)。


このようなファイルの場合:

$ cat file.txt 
1: october rust
2: november rain
3: december snow

Lotherのソリューションからのこのフォークは正常に機能します。

#!/usr/bin/python3.4

with open("file.txt","r+") as f:
    new_f = f.readlines()
    f.seek(0)
    for line in new_f:
        if "snow" not in line:
            f.write(line)
    f.truncate()

改善点:

  • with open、の使用を破棄します f.close()
  • if/else現在の行に文字列が存在しないかどうかを評価するためのより明確な

f.seek(0)が必要な場合?
yifan

@yifanはい。それ以外の場合は、ファイルを上書きするのではなく、ファイルをそれ自体に追加します(除外する行はありません)。
ボリス

5

最初のパスで行を読み取り、2番目のパスで変更(特定の行を削除)を行う際の問題は、ファイルサイズが大きい場合、RAMが不足することです。代わりに、より適切な方法は、行を1つずつ読み取り、それらを別のファイルに書き込んで、不要な行を削除することです。私はこのアプローチを12〜50 GBのファイルで実行しましたが、RAMの使用量はほぼ一定のままです。CPUサイクルのみが進行中の処理を示します。


2

この回答で説明されているfileinputアプローチが好きでした: テキストファイルからの行の削除(python)

たとえば、空の行が含まれているファイルがあり、空の行を削除したいとします。解決方法は次のとおりです。

import fileinput
import sys
for line_number, line in enumerate(fileinput.input('file1.txt', inplace=1)):
    if len(line) > 1:
            sys.stdout.write(line)

注:私の場合の空の行の長さは1でした


2

Linuxを使用している場合は、次の方法を試すことができます。
次の名前のテキストファイルがあるとしますanimal.txt

$ cat animal.txt  
dog
pig
cat 
monkey         
elephant  

最初の行を削除します。

>>> import subprocess
>>> subprocess.call(['sed','-i','/.*dog.*/d','animal.txt']) 

その後

$ cat animal.txt
pig
cat
monkey
elephant

7
このソリューションはOSに依存せず、OPはオペレーティングシステムを指定しなかったため、Linux固有の回答imoを投稿する理由はありません。
Steinar Lima 2016

2
pythonだけで実行できるすべてのことに対してサブプロセスを使用することを提案する人は誰でも反対票を得ます!そして、@ SteinarLimaへの+1 ...同意します
ジェイミーリンジー

2

ファイルをリストに読み込んだ場合は、リストを反復処理して、削除したいニックネームを探すことができると思います。追加のファイルを作成しなくても、非常に効率的に行うことができますが、結果をソースファイルに書き戻す必要があります。

これが私がこれを行う方法です:

import, os, csv # and other imports you need
nicknames_to_delete = ['Nick', 'Stephen', 'Mark']

私は次のnicknames.csvようなデータが含まれていると仮定しています:

Nick
Maria
James
Chris
Mario
Stephen
Isabella
Ahmed
Julia
Mark
...

次に、ファイルをリストにロードします。

 nicknames = None
 with open("nicknames.csv") as sourceFile:
     nicknames = sourceFile.read().splitlines()

次に、リストを反復処理して、削除する入力と一致させます。

for nick in nicknames_to_delete:
     try:
         if nick in nicknames:
             nicknames.pop(nicknames.index(nick))
         else:
             print(nick + " is not found in the file")
     except ValueError:
         pass

最後に、結果をファイルに書き戻します。

with open("nicknames.csv", "a") as nicknamesFile:
    nicknamesFile.seek(0)
    nicknamesFile.truncate()
    nicknamesWriter = csv.writer(nicknamesFile)
    for name in nicknames:
        nicknamesWriter.writeRow([str(name)])
nicknamesFile.close()

1

一般的にはできません。(少なくとも変更の時点から最後まで)ファイル全体を再度書き込む必要があります。

いくつかの特定のケースでは、これよりもうまくいくことができます-

すべてのデータ要素が同じ長さで特定の順序になっておらず、削除したいもののオフセットがわかっている場合は、削除するものの上に最後の項目をコピーし、最後の項目の前のファイルを切り捨てることができます;

または、データチャンクを「これは不良データです。スキップします」の値で上書きするか、保存したデータ要素に「このアイテムは削除されました」フラグを保持して、ファイルを変更せずに削除済みとしてマークすることができます。

これはおそらく、短いドキュメント(100 KB未満のもの)には過剰です。


1

おそらく、あなたはすでに正しい答えを得ていますが、これは私のものです。フィルター処理されていないデータを収集するためにリストを使用する代わりに(readlines()方法は何をするか)、2つのファイルを使用します。1つはメインデータを保持するためのもので、もう1つは特定の文字列を削除するときにデータをフィルター処理するためのものです。ここにコードがあります:

main_file = open('data_base.txt').read()    # your main dataBase file
filter_file = open('filter_base.txt', 'w')
filter_file.write(main_file)
filter_file.close()
main_file = open('data_base.txt', 'w')
for line in open('filter_base'):
    if 'your data to delete' not in line:    # remove a specific string
        main_file.write(line)                # put all strings back to your db except deleted
    else: pass
main_file.close()

これがお役に立てば幸いです。:)


0

ファイル行をリストに保存してから、削除する行をリストから削除し、残りの行を新しいファイルに書き込みます

with open("file_name.txt", "r") as f:
    lines = f.readlines() 
    lines.remove("Line you want to delete\n")
    with open("new_file.txt", "w") as new_f:
        for line in lines:        
            new_f.write(line)

答えを出すとき、なぜあなたの答えがその答えであるのかについていくつかの説明を与えることが望ましいです。
スティーブンラウフ2017

ファイルが改行で終わっていない場合、このコードは、削除する単語が含まれていても、最後の行を削除しません。
ボリス

0

次に、ファイルから行を削除する他の方法をいくつか示します。

src_file = zzzz.txt
f = open(src_file, "r")
contents = f.readlines()
f.close()

contents.pop(idx) # remove the line item from list, by line number, starts from 0

f = open(src_file, "w")
contents = "".join(contents)
f.write(contents)
f.close()

0

私はfileinputと 'inplace'メソッドを使用したこのメソッドが好きです:

import fileinput
for line in fileinput.input(fname, inplace =1):
    line = line.strip()
    if not 'UnwantedWord' in line:
        print(line)

それは他の答えよりも少し言葉が少なく、十分に速いです


0

reライブラリを使用できます

完全なtxtファイルをロードできると仮定します。次に、不要なニックネームのリストを定義し、空の文字列 ""で置き換えます。

# Delete unwanted characters
import re

# Read, then decode for py2 compat.
path_to_file = 'data/nicknames.txt'
text = open(path_to_file, 'rb').read().decode(encoding='utf-8')

# Define unwanted nicknames and substitute them
unwanted_nickname_list = ['SourDough']
text = re.sub("|".join(unwanted_nickname_list), "", text)

-1

ファイルの特定の行を行番号で削除するには:

変数filenameline_to_deleteを、削除するファイルの名前と行番号に置き換えます。

filename = 'foo.txt'
line_to_delete = 3
initial_line = 1
file_lines = {}

with open(filename) as f:
    content = f.readlines() 

for line in content:
    file_lines[initial_line] = line.strip()
    initial_line += 1

f = open(filename, "w")
for line_number, line_content in file_lines.items():
    if line_number != line_to_delete:
        f.write('{}\n'.format(line_content))

f.close()
print('Deleted line: {}'.format(line_to_delete))

出力例:

Deleted line: 3

for nb, line in enumerate(f.readlines())
Dionys

-3

ファイルの内容を取り、改行でタプルに分割します。次に、タプルの行番号にアクセスし、結果のタプルを結合して、ファイルに上書きします。


6
(1)どういう意味tuple(f.read().split('\n'))ですか?(2)「タプルの行番号にアクセスする」と「結果のタプルに参加する」は、かなり不思議な音です。実際のPythonコードの方がわかりやすいかもしれません。
John Machin、2011年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.