Вопрос

Как мне сделать разницу между двумя строками или массивами в Ruby?

Это было полезно?

Решение

diff.rb — это то, что вам нужно, оно доступно по адресу http://users.cybercity.dk/~dsl8950/ruby/diff.html из интернет-архива:

http://web.archive.org/web/20140421214841/http://users.cybercity.dk:80/~dsl8950/ruby/diff.html

Другие советы

Для массивов используйте оператор минус.Например:

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

Здесь последняя строка удаляет из foo все, что также есть в goo, оставляя только элемент 1.Я не знаю, как это сделать для двух строк, но пока кто-нибудь, кто знает, не напишет об этом, вы можете просто преобразовать каждую строку в массив, использовать оператор минус, а затем преобразовать результат обратно.

Меня расстроило отсутствие хорошей библиотеки для этого в Ruby, поэтому я написал http://github.com/samg/diffy.Оно использует diff под обложками и ориентирован на удобство и предоставление привлекательных вариантов вывода.

Что касается струн, я бы сначала попробовал Ruby Gem, о котором @sam-saffron упомянул ниже.Легче установить: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

HTMLDiff, упомянутый выше @da01, сработал для меня.

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; }

Выглядит довольно хорошо.Кстати, я использовал это с acts_as_audited плагин.

А также есть diff-lcs который доступен как драгоценный камень. Он не обновлялся с 2004 года, но мы использовали его без каких-либо проблем.

Редактировать: Новая версия вышла в 2011 году.Похоже, он снова в активной разработке.

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

t=s2.chars; s1.chars.map{|c| c == t.shift ? c : '^'}.join

Эта простая строка дает ^ в позициях, которые не совпадают.Этого достаточно часто, и его можно копировать/вставлять.

Я только что нашел новый проект, который кажется довольно гибким:

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

Пробую и постараюсь опубликовать какой-нибудь отчет.

У меня были такие же сомнения, и найденное мной решение не является 100% Ruby, но является лучшим для меня.Проблема с diff.rb заключается в том, что у него нет красивого форматтера, который мог бы отображать различия в более гуманном виде.Поэтому я использовал diff от ОС с этим кодом:

 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

Просто для пользы пользователей Windows:diffy выглядит великолепно, но я уверен, что он будет работать только на *nix (поправьте меня, если я ошибаюсь).Конечно, на моей машине это не сработало.

Мне помогло отличие (Windows 7 x64, Ruby 1.8.7).

Возможно, поможет Array.diff через Monkey-Patch...

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

Чтобы получить разрешение посимвольно, я добавил новую функцию в жемчужина Дамерау-Левенштейна

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"]

или с разбором:

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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top