diferenciar una cadena o matriz de rubí
Pregunta
¿Cómo hago una diferencia de dos cadenas o matrices en Ruby?
Solución
diff.rb es lo que quieres, que está disponible en http://users.cybercity.dk/~dsl8950/ruby/diff.html a través de archivo de Internet:
http://web.archive.org/web/20140421214841/http://users.cybercity.dk:80/~dsl8950/ruby/diff.html
Otros consejos
Para matrices, utilice el operador menos.Por ejemplo:
>> foo = [1, 2, 3]
=> [1, 2, 3]
>> goo = [2, 3, 4]
=> [2, 3, 4]
>> foo - goo
=> [1]
Aquí la última línea elimina todo de foo que también está en goo, dejando solo el elemento 1.No sé cómo hacer esto para dos cadenas, pero hasta que alguien que sepa lo publique, puede convertir cada cadena en una matriz, usar el operador menos y luego volver a convertir el resultado.
Me sentí frustrado por la falta de una buena biblioteca para esto en Ruby, así que escribí http://github.com/samg/diffy.Usa diff
bajo las sábanas, y se enfoca en ser conveniente y brindar bonitas opciones de salida.
Para las cuerdas, primero probaría Ruby Gem que @sam-saffron menciona a continuación.Es más fácil de instalar: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
El HTMLDiff que @ da01 menciona anteriormente funcionó para mí.
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; }
Se ve bastante bien.Por cierto, usé esto con el acts_as_audited
enchufar.
También hay diff-lcs
que está disponible como una joya. No se ha actualizado desde 2004, pero Lo hemos estado usando sin ningún problema.
Editar: En 2011 se lanzó una nueva versión.Parece que ha vuelto a estar en desarrollo activo.
t=s2.chars; s1.chars.map{|c| c == t.shift ? c : '^'}.join
Esta simple línea da una ^
en las posiciones que no coinciden.Eso es suficiente y se puede copiar y pegar.
Acabo de encontrar un nuevo proyecto que parece bastante flexible:
http://github.com/pvande/differ/tree/master
Probándolo e intentaré publicar algún tipo de informe.
Yo tenía la misma duda y la solución que encontré no es 100% ruby, pero es la mejor para mí.El problema con diff.rb es que no tiene un formateador bonito para mostrar las diferencias de forma humanizada.Entonces usé diff del sistema operativo con este código:
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
Sólo para beneficio de la gente de Windows:diffy se ve brillante pero creo que sólo funcionará en *nix (corríjame si me equivoco).Ciertamente no funcionó en mi máquina.
Differ funcionó de maravilla para mí (Windows 7 x64, Ruby 1.8.7).
Quizás Array.diff a través de Monkey-Patch ayude...
http://grosser.it/2011/07/07/ruby-array-diffother-difference-between-2-arrays/
Para obtener resolución carácter por carácter, agregué una nueva función a joya 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 análisis:
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