2009-04-21 12 views
19

Quando si confrontano linee simili, voglio mettere in evidenza le differenze sulla stessa linea:Python difflib: evidenziando le differenze in linea?

a) lorem ipsum dolor sit amet 
b) lorem foo ipsum dolor amet 

lorem <ins>foo</ins> ipsum dolor <del>sit</del> amet 

Mentre difflib.HtmlDiff sembra fare questo tipo di linea evidenziazione, produce markup molto dettagliata.

Sfortunatamente, non sono stato in grado di trovare un'altra classe/metodo che non funziona su base riga-per-riga.

Mi manca qualcosa? Qualsiasi suggerimento sarebbe apprezzato!

risposta

35

Per la vostra semplice esempio:

import difflib 
def show_diff(seqm): 
    """Unify operations between two compared strings 
seqm is a difflib.SequenceMatcher instance whose a & b are strings""" 
    output= [] 
    for opcode, a0, a1, b0, b1 in seqm.get_opcodes(): 
     if opcode == 'equal': 
      output.append(seqm.a[a0:a1]) 
     elif opcode == 'insert': 
      output.append("<ins>" + seqm.b[b0:b1] + "</ins>") 
     elif opcode == 'delete': 
      output.append("<del>" + seqm.a[a0:a1] + "</del>") 
     elif opcode == 'replace': 
      raise NotImplementedError, "what to do with 'replace' opcode?" 
     else: 
      raise RuntimeError, "unexpected opcode" 
    return ''.join(output) 

>>> sm= difflib.SequenceMatcher(None, "lorem ipsum dolor sit amet", "lorem foo ipsum dolor amet") 
>>> show_diff(sm) 
'lorem<ins> foo</ins> ipsum dolor <del>sit </del>amet' 

questo funziona con le stringhe. Dovresti decidere cosa fare con gli opcode "replace".

+0

Grazie mille per questo! Questo è esattamente il tipo di campione di cui avevo bisogno. Non avevo idea di come iniziare, ma questo lo illustra molto bene. Ancora, molte grazie! – AnC

+0

+1 grazie per il tuo esempio :) Che cosa suggeriresti di fare con la sostituzione di optcodes? – Viet

+0

Bene, un suggerimento sarebbe quello di scoprire alcuni opcode "di sostituzione" in natura; la documentazione dice che possono essere prodotti, ma non ricordo di averne mai visto (IIRC ho visto solo 'delete's seguito da' insert's). In ogni caso, cosa fare con 'sostituzione è fino all'OP. – tzot

2

difflib.SequenceMatcher funziona su linee singole. È possibile utilizzare gli "opcode" per determinare come modificare la prima riga per renderla la seconda riga.

+1

Temo non capisco questo - ancora comunque, così farò altro scavo. Grazie. – AnC

+0

Che cosa stai cercando di fare esattamente con le differenze? Vuoi un output HTML o stavi semplicemente usando HtmlDiff perché ha fatto la diffusione in-line? – Adam

+0

Mentre l'output HTML è il mio caso d'uso principale, l'output di HtmlDiff non consente un facile riutilizzo, cioè se si inserissero semplicemente INS e DEL, che potrebbero quindi essere facilmente trasformati in tutto ciò che è necessario ulteriormente lungo la linea. – AnC

0

Ecco un inline differiscono ispirati da @ di tzot answer above (anche Python 3 compatibile)

def inline_diff(a, b): 
    import difflib 
    matcher = difflib.SequenceMatcher(None, a, b) 
    def process_tag(tag, i1, i2, j1, j2): 
     if tag == 'replace': 
      return '{' + matcher.a[i1:i2] + ' -> ' + matcher.b[j1:j2] + '}' 
     if tag == 'delete': 
      return '{- ' + matcher.a[i1:i2] + '}' 
     if tag == 'equal': 
      return matcher.a[i1:i2] 
     if tag == 'insert': 
      return '{+ ' + matcher.b[j1:j2] + '}' 
     assert false, "Unknown tag %r"%tag 
    return ''.join(process_tag(*t) for t in matcher.get_opcodes()) 

Non è perfetto - ad esempio, sarebbe bello per espandere 'sostituire' codici operativi per riconoscere la parola completa sostituito invece di poche lettere diverse, ma è un buon punto di partenza.

Esempio di output:

>>> a='Lorem ipsum dolor sit amet consectetur adipiscing' 
>>> b='Lorem bananas ipsum cabbage sit amet adipiscing' 
>>> print(inline_diff(a, b)) 
Lorem{+ bananas} ipsum {dolor -> cabbage} sit amet{- consectetur} adipiscing 
Problemi correlati