diff une chaîne ou un tableau de rubis
Question
Comment puis-je créer un diff de deux chaînes ou tableaux en Ruby?
La solution
diff.rb est ce que vous voulez, disponible sur http : //users.cybercity.dk/~dsl8950/ruby/diff.html via l'archive Internet:
http://web.archive.org/web/20140421214841/http://users.cybercity.dk:80/~dsl8950/ruby/diff.html
Autres conseils
Pour les tableaux, utilisez l'opérateur moins. Par exemple:
>> foo = [1, 2, 3]
=> [1, 2, 3]
>> goo = [2, 3, 4]
=> [2, 3, 4]
>> foo - goo
=> [1]
Ici, la dernière ligne supprime tout de foo qui est également dans goo, ne laissant que l'élément 1. Je ne sais pas comment faire cela pour deux chaînes, mais jusqu'à ce que quelqu'un qui connaisse les posts à ce sujet puisse convertir chacun chaîne en un tableau, utilisez l'opérateur moins, puis convertissez le résultat en retour.
Je me suis frustré de l’absence d’une bonne bibliothèque pour cela en ruby, j’ai donc écrit http: // github. com / samg / diffy . Il utilise diff
sous les couvertures et cherche avant tout à être pratique et à fournir de jolies options de sortie.
Pour les chaînes, je voudrais d’abord essayer le Ruby Gem @ sam-saffron mentionné ci-dessous. C'est plus facile à installer: 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
Le HTMLDiff mentionné par @ da01 ci-dessus a fonctionné pour moi.
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; }
Ça a l'air bien. A propos, j’ai utilisé cela avec le plugin acts_as_audited
.
Il existe également diff-lcs
disponible sous forme de gemme. Il n'a pas été mis à jour depuis 2004 mais nous l'utilisons sans problème.
Modifier: Une nouvelle version a été publiée en 2011. On dirait qu'elle est de retour dans le développement actif.
t=s2.chars; s1.chars.map{|c| c == t.shift ? c : '^'}.join
Cette simple ligne donne un ^
dans les positions qui ne correspondent pas. C'est assez souvent et c'est copiable / collable.
Je viens de trouver un nouveau projet qui semble assez flexible:
http://github.com/pvande/differ/tree/master
Essayez-le et essayerez de poster une sorte de rapport.
J'avais le même doute et la solution que j'ai trouvée n'est pas le rubis à 100%, mais le meilleur pour moi. Le problème avec diff.rb est qu’il n’a pas un joli formateur, pour montrer les diffs de façon humanisée. J'ai donc utilisé diff du système d'exploitation avec ce code:
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
Juste pour le bénéfice des utilisateurs de Windows: diffy a l’air brillant, mais je crois que cela ne fonctionnera que sur * nix (corrigez-moi si je me trompe). Cela n'a certainement pas fonctionné sur ma machine.
Differ a fonctionné à merveille pour moi (Windows 7 x64, Ruby 1.8.7).
Peut-être qu'Array.diff via monkey-patch aide ...
http://grosser.it / 2011/07/07 / ruby-array-diffother-difference-between-2-arrays /
Pour obtenir une résolution caractère par caractère, j'ai ajouté une nouvelle fonction à la gem 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"]
ou avec analyse:
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