2013-04-29 3 views
0

TinyMCE에서 제작 한 HTML 본문에 대해 wiki와 비슷한 차이 기능을 개발 중입니다. diff-lcs은 배열이나 객체를 허용하는 차별화 된 보석입니다. 대부분의 차이점은 코드에 있으며 단지 라인을 비교합니다. HTML을 타는 텍스트의 바디 차이는 더욱 복잡합니다. 텍스트 본문을 꽂으면 문자 비교를 통해 문자를 얻습니다. 결과가 정확할지라도 쓰레기처럼 보일 것입니다.HTML 문자열을 배열로 구문 분석합니다.

seq1 = "<p>Here is a paragraph. A sentence with <strong>bold text</strong>.</p><p>The second paragraph.</p>" 

seq2 = seq1.gsub(/[.!?]/, '\0|').split('|') 
=> ["<p>Here is a paragraph.", " A sentence with <strong>bold text</strong>.", "</p><p>The second paragraph.", "</p>"] 

누군가가 두 번째 단락을 변경하면 차이 출력에는 이전 단락 끝 태그가 포함됩니다. 내가 비교보기에 서식을 유지하고 싶기 때문에 나는 단지 strip_tags을 사용할 수 없다. 이상적인 비교는 완전한 문장을 기반으로 HTML을 분리 한 것입니다.

seq2.NokogiriMagic 
=> ["<p>", "Here is a paragraph.", " A sentence with ", "<strong>", "bold text", "</strong>", ".", "</p>", "<p>", "The second paragraph.", "</p>"] 

은 내가 위를 않습니다 발견했습니다 깔끔한 노코 기리 방법 아무것도하지만, 많이 발견했다.

+0

Nokogiri는 XML/HTML을 구문 분석하도록 설계되었으므로 문자열의 배열 인 'seq2'의 시작점은 Nokogiri를 적절하게 사용하지 않습니다. TinyMCE의 전체 출력은 얼마입니까? 루트 요소가 있습니까? –

+0

TinyMCE의 출력은 seq1과 유사합니다. seq2는 중요하지 않습니다. seq3과 같은 형식을 사용하고 싶습니다. 나는 어린이를위한 Nokogiri 객체를 파싱해야 할 것 같고 seq2와 같은 작업을 수행합니다. – Archonic

+0

SAX 인터페이스를 사용하여 모든 태그를 배열에 추가하고 텍스트 노드의 경우 단어를 분할 할 수 있습니다. –

답변

3

당신이 SAX parser 함께 할 수있는 방법은 다음과 같습니다 당신이 그렇게 루트 요소에 HTML을 포장해야합니다

require 'nokogiri' 

html = "<p>Here is a paragraph. A sentence with <strong>bold text</strong>.</p><p>The second paragraph.</p>" 

class ArraySplitParser < Nokogiri::XML::SAX::Document 
    attr_reader :array 
    def initialize; @array = []; end 
    def start_element(name, attrs=[]) 
    tag = "<" + name 
    attrs.each { |k,v| tag += " #{k}=\"#{v}\"" } 
    @array << tag + ">" 
    end 
    def end_element(name); @array << "</#{name}>"; end 
    def characters(str); @array += str.gsub(/\s/, '\0|').split('|'); end 
end 

parser = ArraySplitParser.new 
Nokogiri::XML::SAX::Parser.new(parser).parse(html) 
puts parser.array.inspect 
# ["<p>", "Here ", "is ", "a ", "paragraph. ", "A ", "sentence ", "with ", "<strong>", "bold ", "text", "</strong>", ".", "</p>"] 

참고 XML 파서는 예제의 두 번째 단락을 놓치지 않습니다. 이런 식으로 뭔가 작업을해야합니다 :

# ... 
Nokogiri::XML::SAX::Parser.new(parser).parse('<x>' + html + '</x>') 
# ... 
puts parser.array[1..-2] 
# ["<p>", "Here ", "is ", "a ", "paragraph. ", "A ", "sentence ", "with ", "<strong>", "bold ", "text", "</strong>", ".", "</p>", "<p>", "The ", "second ", "paragraph.", "</p>"] 

[편집]는 요소가 "START_ELEMENT"방법 속성을 유지하는 방법을 보여줍니다 업데이트되었습니다.

+0

'ArraySplitParser'에 태그 속성을 보존하기 위해 추가 할 수있는 아이디어가 있습니까? – Archonic

+0

@Archonic 힌트 :'start_element' 메쏘드의 매개 변수를보십시오. 파서는'attrs' 변수를 전달합니다. –

+0

답변을 업데이트했습니다. attrs를 파싱했는데'@ array'에 쓰지 않았습니다. – Archonic

2

당신은 관용적 인 루비에 코드를 쓰지 않을 것입니다. 변수 이름에 대문자와 소문자가 혼용되지 않으며 일반적으로 프로그래밍에서 명료성을 위해 니모닉 변수 이름을 사용하는 것이 좋습니다. 코드를 리팩토링하는 것은 내가 그것을 써서 어떻게이어야합니다 :

tags = %w[p ol ul li h6 h5 h4 h3 h2 h1 em strong i b table thead tbody th tr td] 
# Deconstruct HTML body 1 
doc = Nokogiri::HTML.fragment(@versionOne.body) 
nodes = doc.css(tags.join(', ')) 

# Reconstruct HTML body 1 into comparable array 
output = [] 
nodes.each do |node| 

    output << [ 
    "<#{ node.name }", 
    node.attributes.map { |param| '%s="%s"' % [param.name, param.value] }.join(' '), 
    '>' 
    ].join 

    output << node.children.to_s.gsub(/[\s.!?]/, '|\0|').split('|').flatten 

    output << "</#{ node.name }>" 

end 

# Same deal for nokoOutput2 

sdiff = Diff::LCS.sdiff(nokoOutput2.flatten, output.flatten) 

라인 : 문자열이 | 연산자가 없기 때문에 코드에서

tag | " #{ param.name }=\"#{ param.value }\" " 

전혀 루비 없습니다. 코드에 | 연산자를 추가 했습니까? 정의를 표시하지 않았습니까?

내가 볼 문제는 다음과 같습니다

output << node.children.to_s.gsub(/[\s.!?]/, '|\0|').split('|').flatten 

목록에있는 다른 태그를 포함 할 수 있습니다 당신이 찾고있는 태그의 많은 :

을 : 처리하는 재귀 적 방법을 만들기

<html> 
    <body> 
    <table><tr><td> 
     <table><tr><td> 
     foo 
     </td></tr></table> 
    </td></tr></table> 
    </body> 
</html> 

node.attributes.map { |param| '%s="%s"' % [param.name, param.value] }.join(' '), 

은 출력을 향상시킬 수 있습니다. 이 안된하지만 일반적인 생각입니다 :

여기
def dump_node(node) 

    output = [ 
    "<#{ node.name }", 
    node.attributes.map { |param| '%s="%s"' % [param.name, param.value] }.join(' '), 
    '>' 
    ].join 

    output += node.children.map{ |n| dump_node(n) } 

    output << "</#{ node.name }>" 

end 
+0

고마워! 이것은 모두 잘 알고 있습니다. 내가 sax 파서와 함께 @maerics 대답이 재귀 적으로 노드를 덤프 할 필요를 피하는 것을 생각할 때 맞습니까? – Archonic

+0

SAX는 본질적으로 각 태그를 연속적으로보고 노드를 동일한 방식으로 출력하는 데 적합합니다. 결과적으로이 목적을 위해 재귀 적으로 노드를 덤프 할 필요가 없습니다. SAX 대 DOM의 장점/단점에 대한 원래 질문에 대한 내 의견을 참조하십시오. –

관련 문제