Domanda
Come faccio a fare una differenza di due stringhe o array in Ruby?
Soluzione
diff.rb è quello che vuoi, che è disponibile su http://users.cybercity.dk/~dsl8950/ruby/diff.html tramite archivio internet:
http://web.archive.org/web/20140421214841/http://users.cybercity.dk:80/~dsl8950/ruby/diff.html
Altri suggerimenti
Per gli array, utilizzare l'operatore meno.Per esempio:
>> foo = [1, 2, 3]
=> [1, 2, 3]
>> goo = [2, 3, 4]
=> [2, 3, 4]
>> foo - goo
=> [1]
Qui l'ultima riga rimuove da foo tutto ciò che è anche in goo, lasciando solo l'elemento 1.Non so come farlo per due stringhe, ma finché qualcuno che lo conosce non pubblica un post a riguardo, potresti semplicemente convertire ogni stringa in un array, utilizzare l'operatore meno e quindi riconvertire il risultato.
Ero frustrato dalla mancanza di una buona libreria per questo in Ruby, quindi ho scritto http://github.com/samg/diffy.Utilizza diff
sotto le coperte e si concentra sulla comodità e sulla fornitura di graziose opzioni di output.
Per gli archi, proverei prima il Ruby Gem menzionato da @sam-saffron di seguito.È più facile da installare:http://github.com/pvande/differ/tree/master
gem install differ
irb
require 'differ'
one = "one two three"
two = "one two 3"
Differ.format = :color
puts Differ.diff_by_word(one, two).to_s
Differ.format = :html
puts Differ.diff_by_word(one, two).to_s
L'HTMLDiff menzionato da @ da01 sopra ha funzionato per me.
script/plugin install git://github.com/myobie/htmldiff.git
# bottom of environment.rb
require 'htmldiff'
# in model
class Page < ActiveRecord::Base
extend HTMLDiff
end
# in view
<h1>Revisions for <%= @page.name %></h1>
<ul>
<% @page.revisions.each do |revision| %>
<li>
<b>Revised <%= distance_of_time_in_words_to_now revision.created_at %> ago</b><BR>
<%= Page.diff(
revision.changes['description'][0],
revision.changes['description'][1]
) %>
<BR><BR>
</li>
<% end %>
# in style.css
ins.diffmod, ins.diffins { background: #d4fdd5; text-decoration: none; }
del.diffmod, del.diffdel { color: #ff9999; }
Sembra piuttosto buono.A proposito, l'ho usato con il acts_as_audited
collegare.
C'è anche diff-lcs
che è disponibile come gemma. Non è stato aggiornato dal 2004 ma lo stiamo usando senza alcun problema.
Modificare: Una nuova versione è stata rilasciata nel 2011.Sembra che sia tornato in fase di sviluppo attivo.
t=s2.chars; s1.chars.map{|c| c == t.shift ? c : '^'}.join
Questa semplice riga dà a ^
nelle posizioni che non corrispondono.È abbastanza spesso ed è copiabile/incollabile.
Ho appena trovato un nuovo progetto che sembra abbastanza flessibile:
http://github.com/pvande/differ/tree/master
Provandolo e proverò a pubblicare una sorta di rapporto.
Avevo lo stesso dubbio e la soluzione che ho trovato non è ruby al 100%, ma per me è la migliore.Il problema con diff.rb è che non ha un bel formattatore, per mostrare le differenze in modo umanizzato.Quindi ho usato diff dal sistema operativo con questo codice:
def diff str1, str2
system "diff #{file_for str1} #{file_for str2}"
end
private
def file_for text
exp = Tempfile.new("bk", "/tmp").open
exp.write(text)
exp.close
exp.path
end
Solo a beneficio degli utenti Windows:diffy sembra geniale ma credo che funzionerà solo su *nix (correggimi se sbaglio).Certamente non ha funzionato sulla mia macchina.
Differ ha funzionato a meraviglia per me (Windows 7 x64, Ruby 1.8.7).
Forse Array.diff tramite Monkey-Patch aiuta...
http://grosser.it/2011/07/07/ruby-array-diffother-difference-between-2-arrays/
Per ottenere la risoluzione carattere per carattere ho aggiunto una nuova funzione a gemma di damerau-levenshtein
require "damerau-levenshtein"
differ = DamerauLevenshtein::Differ.new
differ.run "Something", "Smothing"
# returns ["S<ins>o</ins>m<subst>e</subst>thing",
# "S<del>o</del>m<subst>o</subst>thing"]
o con l'analisi:
require "damerau-levenshtein"
require "nokogiri"
differ = DamerauLevenshtein::Differ.new
res = differ.run("Something", "Smothing!")
nodes = Nokogiri::XML("<root>#{res.first}</root>")
markup = nodes.root.children.map do |n|
case n.name
when "text"
n.text
when "del"
"~~#{n.children.first.text}~~"
when "ins"
"*#{n.children.first.text}*"
when "subst"
"**#{n.children.first.text}**"
end
end.join("")
puts markup