2012-10-06 113 views
9

我有~200個短文本文件(50kb),都有類似的格式。我想在包含某個字符串的每個文件中找到一行,然後將該行加上接下來的三行(但不是文件中的其餘行)寫入另一個文本文件。我試圖教自己python爲了做到這一點,並寫了一個非常簡單和粗略的小腳本來嘗試這一點。我使用的版本2.6.5,並運行在Mac端腳本:使用python從一個文件寫入特定的行到另一個文件

#!/usr/bin/env python 

f = open('Test.txt') 

Lines=f.readlines() 
searchquery = 'am\n' 
i=0 

while i < 500: 
    if Lines[i] == searchquery: 
     print Lines[i:i+3] 
     i = i+1 
    else: 
     i = i+1 
f.close() 

這或多或少的作品,並打印輸出到屏幕上。但我想打印行到一個新的文件,所以我嘗試這樣的:

f1 = open('Test.txt') 
f2 = open('Output.txt', 'a') 

Lines=f1.readlines() 
searchquery = 'am\n' 
i=0 

while i < 500: 
if Lines[i] == searchquery: 
    f2.write(Lines[i]) 
    f2.write(Lines[i+1]) 
    f2.write(Lines[i+2]) 
    i = i+1 
else: 
    i = i+1 
f1.close() 
f2.close() 

但是,沒有什麼被寫入文件。我也試過

from __future__ import print_function 
print(Lines[i], file='Output.txt') 

並且無法讓它工作。如果任何人都可以解釋我做錯了什麼,或者提供一些關於我應該嘗試的建議,我會非常感激。此外,如果您有任何改善搜索的建議,我也會很感激。我一直在使用一個測試文件,其中我想查找的字符串是行中唯一的文本,但在我的真實文件中,我需要的字符串仍然在行首,但後面跟着一堆其他文本,所以我認爲我現在設置的東西的方式也不會有效。

謝謝,對不起,如果這是一個超級基本的問題!

+2

在第二個例子中,它看起來像你的循環體沒有縮進..是複製/粘貼錯誤還是你實際上有什麼? – Collin

+2

您可能應該查看'enumerate'函數和'for iterable'構造。 –

+0

@Collin你是對的問題是縮進。我可能看了兩個小時的代碼,從未注意到!謝謝! – Andreanna

回答

17

正如@ajon指出,我不認爲有什麼根本性的錯誤代碼,除了縮進。隨着固定的縮進,它適用於我。但是有一些改進的機會。

1)在Python中,迭代事物的標準方法是使用for loop。當使用for循環時,您不需要定義循環計數器變量並自己跟蹤它們以迭代事物。相反,你寫這樣的東西

for line in lines: 
    print line 

遍歷字符串列表中的所有項目並打印它們。

2)在大多數情況下,這就是您的for循環的樣子。但是,在某些情況下,您確實需要跟蹤循環計數。你的情況就是這樣的情況,因爲你不僅需要這一行,而且還需要這三行,因此需要使用計數器進行索引(lst[i])。爲此,有enumerate(),它將返回項目列表其索引,然後您可以循環。

for i, line in enumerate(lines): 
    print i 
    print line 
    print lines[i+7] 

如果你要手動跟蹤循環計數器在您的例子中,有兩件事情:

3)i = i+1應移出ifelse塊。你在這兩種情況下都這樣做,所以把它放在if/else之後。在你的情況else塊則沒有做任何事,而且可以消除:

while i < 500: 
    if Lines[i] == searchquery: 
     f2.write(Lines[i]) 
     f2.write(Lines[i+1]) 
     f2.write(Lines[i+2]) 
    i = i+1 

4)現在,這將導致IndexError比500線較短的文件。而不是硬編碼循環計數500,您應該使用您正在迭代的序列的實際長度。 len(lines)會給你那麼長的時間。但不是使用while循環,而是使用for循環和range(len(lst))來重複範圍從零到len(lst) - 1的列表。

for i in range(len(lst)): 
    print lst[i] 

5)open()可以用作context manager這需要關閉文件你的照顧。上下文管理器是一個相當先進的概念,但是如果它們已經爲您提供,使用起來非常簡單。通過做這樣的事情

with open('test.txt') as f: 
    f.write('foo') 

該文件將被打開,並訪問您的fwith塊內。離開程序塊後,文件將自動關閉,所以最終不會忘記關閉文件。

在你的情況下,你打開兩個文件。這可以通過只使用兩個with語句和嵌套來完成他們

with open('one.txt') as f1: 
    with open('two.txt') as f2: 
     f1.write('foo') 
     f2.write('bar') 

,或者在Python 2.7/Python的3.x中,通過在單一with聲明嵌套兩次上下文經理:

with open('one.txt') as f1, open('two.txt', 'a') as f2: 
     f1.write('foo') 
     f2.write('bar') 

6)根據創建文件的操作系統,行結束是不同的。在類似UNIX的平臺上,它是\n,OS X使用之前的Macs \r,而Windows使用\r\n。因此,Lines[i] == searchquery將不匹配Mac或Windows行尾。 file.readline()可以處理所有這三個,但因爲它保留了行結束處的所有行結束符,所以比較將失敗。

searchquery = 'am' 
# ... 
      if line.strip() == searchquery: 
       # ... 

(閱讀使用file.read()文件和:這是通過使用str.strip(),這將剝離開頭和末尾的所有空格的字符串,比較搜索模式沒有結束,以該行解決使用str.splitlines()將是另一種選擇。)

但是,既然你提到你的搜索字符串實際上出現在一行的開頭,讓我們做到這一點,利用str.startswith()

if line.startswith(searchquery): 
    # ... 

7)官方風格指南的Python,PEP8,建議使用CamelCase作爲類別,lowercase_underscore作爲幾乎所有其他事物(變量,函數,屬性,方法,模塊,包)。因此,而不是Lines使用lines。與其他人相比,這絕對是一個小問題,但仍然值得在早期進行。


因此,考慮到所有這些事情,我會寫你這樣的代碼:

searchquery = 'am' 

with open('Test.txt') as f1: 
    with open('Output.txt', 'a') as f2: 
     lines = f1.readlines() 
     for i, line in enumerate(lines): 
      if line.startswith(searchquery): 
       f2.write(line) 
       f2.write(lines[i + 1]) 
       f2.write(lines[i + 2]) 

由於@TomK指出,所有這些代碼假設,如果你的搜索字符串匹配,有至少兩條線跟着它。如果你不能依靠這個假設,那麼通過使用像@poorsod這樣的try...except塊來處理這種情況是正確的。

+2

哇,這非常有幫助。感謝您的詳細解釋! – Andreanna

2

我認爲你的問題是底部文件的選項卡。

你需要從縮進如果Lines[i]後才i=i+1如:

while i < 500: 
    if Lines[i] == searchquery: 
     f2.write(Lines[i]) 
     f2.write(Lines[i+1]) 
     f2.write(Lines[i+2]) 
     i = i+1 
    else: 
     i = i+1 
1

ajon有正確的答案,但只要您在尋找指導,您的解決方案不會利用Python可提供的高級構造。如何:

searchquery = 'am\n' 

with open('Test.txt') as f1: 
    with open(Output.txt, 'a') as f2: 

    Lines = f1.readlines() 

    try: 
     i = Lines.index(searchquery) 
     for iline in range(i, i+3): 
     f2.write(Lines[iline]) 
    except: 
     print "not in file" 

即使發生異常,兩個「with」語句也會在最後自動關閉文件。

的靜止更好的解決辦法是避免讀取整個文件一次(誰知道有多大可能呢?),取而代之的,由線工藝路線,使用一個文件對象上迭代:

with open('Test.txt') as f1: 
    with open(Output.txt, 'a') as f2: 
     for line in f1: 
     if line == searchquery: 
      f2.write(line) 
      f2.write(f1.next()) 
      f2.write(f1.next()) 

所有這些都假定您的目標線以外至少有兩條附加線。

+0

「所有這些都假定您的目標線以外至少有兩條附加線。」 - 值得指出的是,處理這種情況的方法是使用['try ... except'塊](http://docs.python.org/reference/compound_stmts.html#the-try-statement) :'try:f2.write(f1.next());除了StopIteration:pass'或類似之外。 –

+0

你應該永遠不使用裸'except:'語句,總是列出你想明確捕捉的異常(在這種情況下是'IndexError')。使用「except:」可能會導致你無法想象的事情,並且需要處理與你期望的情況不同的事情。在這種情況下,應該提出異常,導致代碼停止而不是繼續並可能導致損壞。 –

1

您是否嘗試過使用「Output.txt」以外的內容來避免任何與文件系統相關的問題?

如何在診斷這種情況時避免任何時髦的無法預料的問題的絕對路徑呢?

此建議僅從診斷角度出發。另外檢查OS X dtrace和dtruss。

見:大數據時Equivalent of strace -feopen <command> on mac os X

0

寫作一行行可能會很慢。您可以通過一次讀取/寫入一堆行來加速讀取/寫入操作。

from itertools import slice 

f1 = open('Test.txt') 
f2 = open('Output.txt', 'a') 

bunch = 500 
lines = list(islice(f1, bunch)) 
f2.writelines(lines) 

f1.close() 
f2.close() 

如果您的線路太長並且取決於您的系統,可能無法將500行放入列表中。如果是這樣的話,你應該減小尺寸,並根據需要儘可能多的讀/寫步驟來編寫整個東西。

相關問題