2013-12-13 14 views
7

Sono su Python 2.7.1 e sto cercando di identificare tutti i file di testo che non lo fanno contengono una certa stringa di testo.Trova tutto il testo non file contenenti una stringa di testo

Inizialmente il programma sembrava funzionare, ma ogni volta che aggiungo la stringa di testo a un file, continua ad apparire come se non lo contenga (falso positivo). Quando controllo il contenuto del file di testo, la stringa è chiaramente presente.

Il codice ho provato a scrivere è

def scanFiles2(rdir,sstring,extens,start = '',cSens = False): 
    fList = [] 
    for fol,fols,fils in os.walk(rdir): 
     fList.extend([os.path.join(rdir,fol,fil) for fil in fils if fil.endswith(extens) and fil.startswith(start)]) 
    if fList: 
     for fil in fList: 
      rFil = open(fil) 
      for line in rFil: 
       if not cSens: 
        line,sstring = line.lower(), sstring.lower() 
       if sstring in line: 
        fList.remove(fil) 
        break 
      rFil.close() 
    if fList: 
     plur = 'files do' if len(fList) > 1 else 'file does' 
     print '\nThe following %d %s not contain "%s":\n'%(len(fList),plur,sstring) 
     for fil in fList: 
      print fil 
    else: 
     print 'No files were found that don\'t contain %(sstring)s.'%locals() 
scanFiles2(rdir = r'C:\temp',sstring = '!!syn',extens = '.html', start = '#', cSens = False) 

Penso che c'è un difetto nel codice, ma io davvero non lo vedo.

UPDATE

Il codice viene ancora con molti falsi positivi: i file che fare contengono la stringa di ricerca, ma sono identificati come non lo contengono.

Potrebbe codificare il codice ? Ho prefisso la stringa di ricerca con U per tenere conto della codifica Unicode ma non ha fatto alcuna differenza.

Python in qualche modo cache contenuto del file? Io non la penso così ma questo potrebbe in qualche modo rendere conto che i file continuano a comparire dopo essere stati corretti.

Potrebbe qualche tipo di virus causare sintomi come questi? Sembra altamente improbabile per me, ma sono piuttosto disperata per farlo correggere.

+0

ho provato così com'è e funziona per me (appena modificato "extens" e "rdir" per abbinare il mio attuale env) –

+0

@le_vine: è fantastico ma per me include ancora una manciata di file che ** ** includi la stringa di ricerca. Dovrei aggiungere che la stringa di ricerca è stata recentemente aggiunta a loro. Qualche idea su cosa potrebbe succedere? Come se Python ottenga il contenuto del file dalla cache anziché dal disco o qualcosa del genere ... – RubenGeert

+0

La convenzione di denominazione utilizzata nel codice non è la migliore. Ci sono troppe 'fil',' fLi' nel codice. Prova a leggere il codice ad alta voce. Prova ad usare i nomi della documentazione per le funzioni corrispondenti, ad es. 'Dirpath, dirnames, nomi di file' invece di' fol, fols, fils' – jfs

risposta

12

elemento Modifica mentre l'iterazione tabulato causare risultati imprevisti:

Ad esempio:

>>> lst = [1,2,4,6,3,8,0,5] 
>>> for n in lst: 
...  if n % 2 == 0: 
...   lst.remove(n) 
... 
>>> lst 
[1, 4, 3, 0, 5] 

Soluzione iterare oltre copiare

>>> lst = [1,2,4,6,3,8,0,5] 
>>> for n in lst[:]: 
...  if n % 2 == 0: 
...   lst.remove(n) 
... 
>>> lst 
[1, 3, 5] 

In alternativa, è possibile aggiungere valida percorso del file, anziché remo ving dall'intero elenco di file.

Versione modificata (file aggiungendo che non si Contian sstring invece di rimuovere):

def scanFiles2(rdir, sstring, extens, start='', cSens=False): 
    if not cSens: 
     # This only need to called once. 
     sstring = sstring.lower() 
    fList = [] 
    for fol, fols, fils in os.walk(rdir): 
     for fil in fils: 
      if not (fil.startswith(start) and fil.endswith(extens)): 
       continue 
      fil = os.path.join(fol, fil) 
      with open(fil) as rFil: 
       for line in rFil: 
        if not cSens: 
         line = line.lower() 
        if sstring in line: 
         break 
       else: 
        fList.append(fil) 
    ... 
  • list.remove prende O (n), mentre list.append prende O (1). Vedi Time Complexity.
  • Utilizzare la dichiarazione with se possibile.
+0

Per trovare i file, considera [glob] (http://docs.python.org/2/ biblioteca/glob.html)? – Ray

+0

@Ray, 'glob.glob' non ricorre. potrebbe fare uso di 'glob.glob', ma l'elenco dei file è già ottenuto da' os.walk', quindi non sembra necessario. Intendi ['fnmatch.fnamtch'] (http://docs.python.org/2/library/fnmatch.html#fnmatch.fnmatch)? – falsetru

+0

Oh ora capisco. 'os.walk' funziona abbastanza bene. :) – Ray

1

Falsetru ti ha già mostrato il motivo per cui non dovresti rimuovere le righe da un elenco mentre lo fai scorrere su di esso; gli iteratori dell'elenco non aggiornano e non possono aggiornare il contatore quando una lista viene abbreviata, quindi se l'elemento 3 è stato elaborato ma l'elemento è stato rimosso, il successivo elemento di iterazione 4 si trovava precedentemente nell'indice 5.

versione di lista usando fnmatch.filter() e any() ed un filtro lambda per caso corrispondenza insensitive:

import fnmatch 

def scanFiles2(rdir, sstring, extens, start='', cSens=False): 
    lfilter = sstring.__eq__ if cSens else lambda l, s=sstring.lower(): l.lower() == s 
    ffilter = '{}*{}'.format(start, extens) 
    return [os.path.join(r, fname) 
      for r, _, f in os.walk(rdir) 
      for fname in fnmatch.filter(f, ffilter) 
      if not any(lfilter(l) for l in open(os.path.join(root, fname)))] 

ma forse sarebbe meglio attenersi ad un ciclo più leggibile:

def scanFiles2(rdir, sstring, extens, start='', cSens=False): 
    lfilter = sstring.__eq__ if cSens else lambda l, s=sstring.lower(): l.lower() == s 
    ffilter = '{}*{}'.format(start, extens) 
    result = [] 
    for root, _, files in os.walk(rdir): 
     for fname in fnmatch.filter(files, ffilter): 
      fname = os.path.join(r, fname) 
      with open(fname) as infh: 
       if not any(lfilter(l) for l in infh): 
        result.append(fname) 
    return result 
1

altro alternativa che apre la ricerca di espressioni regolari (anche se l'utilizzo di grep con le opzioni appropriate sarebbe comunque migliore):

import mmap 
import os 
import re 
import fnmatch 

def scan_files(rootdir, search_string, extension, start='', case_sensitive=False): 
    rx = re.compile(re.escape(search_string), flags=re.I if not case_sensitive else 0) 
    name_filter = start + '*' + extension 
    for root, dirs, files in os.walk(rootdir): 
     for fname in fnmatch.filter(files, name_filter): 
      with open(os.path.join(root, fname)) as fin: 
       try: 
        mm = mmap.mmap(fin.fileno(), 0, access=mmap.ACCESS_READ) 
       except ValueError: 
        continue # empty files etc.... include this or not? 
       if not next(rx.finditer(mm), None): 
        yield fin.name 

Quindi utilizzare list su che se si desidera che i nomi materializzati o trattarlo come si farebbe con qualsiasi altro generatore ...

1

Si prega di non scrivere un programma Python per questo. Questo programma esiste già. Uso grep:

grep * -Ilre 'main' 2> /dev/null 
99client/.git/COMMIT_EDITMSG 
99client/taxis-android/build/incremental/mergeResources/production/merger.xml 
99client/taxis-android/build/incremental/mergeResources/production/inputs.data 
99client/taxis-android/build/incremental/mergeResources/production/outputs.data 
99client/taxis-android/build/incremental/mergeResources/release/merger.xml 
99client/taxis-android/build/incremental/mergeResources/release/inputs.data 
99client/taxis-android/build/incremental/mergeResources/release/outputs.data 
99client/taxis-android/build/incremental/mergeResources/debug/merger.xml 
99client/taxis-android/build/incremental/mergeResources/debug/inputs.data 
(...) 

http://www.gnu.org/savannah-checkouts/gnu/grep/manual/grep.html#Introduction

Se avete bisogno della lista in Python, è sufficiente eseguire grep da esso e raccogliere il risultato.

Problemi correlati