diff uma string ou matriz de ruby

Como eu faço um diff de duas strings ou arrays em Ruby?

Para matrizes, use o operador menos. Por exemplo:

>> foo = [1, 2, 3] => [1, 2, 3] >> goo = [2, 3, 4] => [2, 3, 4] >> foo - goo => [1] 

Aqui a última linha remove tudo de foo que também está em goo, deixando apenas o elemento 1. Eu não sei como fazer isso por duas strings, mas até alguém que saiba posts sobre isso, você poderia simplesmente converter cada string para um array, use o operador menos e converta o resultado de volta.

Eu fiquei frustrado com a falta de uma boa biblioteca para isso em Ruby, então eu escrevi http://github.com/samg/diffy . Ele usa diff sob as capas e concentra-se em ser conveniente e fornecer ótimas opções de saída.

Para strings, eu primeiro tentaria a Ruby Gem que @ sam-saffron mencionada abaixo. É mais 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 

O HTMLDiff que @ da01 menciona acima funcionou para mim.

 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 

Revisions for <%= @page.name %>

    <% @page.revisions.each do |revision| %>
  • Revised <%= distance_of_time_in_words_to_now revision.created_at %> ago
    <%= Page.diff( revision.changes['description'][0], revision.changes['description'][1] ) %>

  • <% end %> # in style.css ins.diffmod, ins.diffins { background: #d4fdd5; text-decoration: none; } del.diffmod, del.diffdel { color: #ff9999; }

Parece muito bom. Pela maneira que eu usei isso com o plugin acts_as_audited .

Há também diff-lcs que está disponível como uma gema. Não foi atualizado desde 2004, mas estamos usando-o sem nenhum problema.

Edit: Uma nova versão foi lançada em 2011. Parece que está de volta ao desenvolvimento ativo.

http://rubygems.org/gems/diff-lcs

Acabei de encontrar um novo projeto que parece bastante flexível:

http://github.com/pvande/differ/tree/master

Tentando e tentarei postar algum tipo de relatório.

Eu tive a mesma dúvida e a solução que encontrei não é 100% ruby, mas é a melhor para mim. O problema com o diff.rb é que ele não possui um formatador bonito, para mostrar os diffs de maneira humanizada. Então eu usei diff do sistema operacional com 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 

Apenas para o benefício das pessoas do Windows: diffy parece shiny, mas acredito que só funcionará no * nix (corrija-me se estiver errado). Certamente não funcionou na minha máquina.

O Differ funcionou como um tratamento para mim (Windows 7 x64, Ruby 1.8.7).

Talvez o Array.diff via patch de macaco ajude …

http://grosser.it/2011/07/07/ruby-array-diffother-difference-between-2-arrays/

Para obter a resolução de caractere por caractere, adicionei uma nova function à gem damerau-levenshtein

 require "damerau-levenshtein" differ = DamerauLevenshtein::Differ.new differ.run "Something", "Smothing" # returns ["Something", # "Somothing"] 

ou com análise:

 require "damerau-levenshtein" require "nokogiri" differ = DamerauLevenshtein::Differ.new res = differ.run("Something", "Smothing!") nodes = Nokogiri::XML("#{res.first}") 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 

t = s2.split (”); s1.split (”). map {| c | c == t.shift? c: ‘^’}.

Esta linha simples dá um ^ nas posições que não combinam. Isso é muitas vezes suficiente e é copiar / colar capaz.