text_alignment 0.2.7 → 0.3.11

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45bf8e55a66daff4d79765aec0e4d7482151aa7b86749e054e5d93030ffffabe
4
- data.tar.gz: 6251835e35077e2ae828666026a663ae63be7f8d7b7d785b4898763021f546e2
3
+ metadata.gz: 01525b6ca5f7e0ae1ebb9dfce2083006e095a66b3ab468ccef0a584bc3005556
4
+ data.tar.gz: bc1137052a12b8db97635b183f299518b0158f3ccecf36d42fa45d746b4f3792
5
5
  SHA512:
6
- metadata.gz: 4750b2507b4eb172123ca9a82320c2456e66d3f06ca3e3bdd52cf31055e9cb46af53013a484dbb87b1bb3236701cc8a69e1c4c64b576ee41a3a4957c3431be65
7
- data.tar.gz: 1819b94e47bdf064d53712bb667f15d74f8e37bd49df7976075c3cbdc0a992d25bb318f76bfc0522cea0b594d002bcab0ebcef6112bf9e26128c245a7fb67bcf
6
+ metadata.gz: 9596bea2616c3b4d939c8314d026941a6e627f4380183409051df62722a0ee5e3b35302da3066b0d32e8322582c999877b05a09c54749d878a284a062247342e
7
+ data.tar.gz: 361e3e7a23697167b41e037e7d272bbd286ac397416defb125821ebbcb17bfa386341c75fecbb94fe7f9976cfbc2a8b5f7f9be7c150d23daf6d8c16410509b5d
@@ -1,51 +1,202 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'text_alignment'
3
-
4
3
  require 'json'
5
4
  require 'pp'
6
5
 
6
+ def read_annotations(filename)
7
+ case File.extname(filename)
8
+ when '.json'
9
+ JSON.parse File.read(filename), :symbolize_names => true
10
+ when '.txt'
11
+ {text: File.read(filename)}
12
+ else
13
+ raise "unknown file type: #{filename}"
14
+ end
15
+ end
16
+
17
+ def read_text(filename)
18
+ case File.extname(filename)
19
+ when '.json'
20
+ json = JSON.parse File.read(filename), :symbolize_names => true
21
+ json[:text]
22
+ when '.txt'
23
+ File.read(filename)
24
+ else
25
+ raise "unknown file type: #{filename}"
26
+ end
27
+ end
28
+
29
+ def align_mdoc(source_annotations, target_annotations)
30
+ idnum_denotations = 0
31
+ idnum_relations = 0
32
+ idnum_attributes = 0
33
+ idnum_modifications = 0
34
+
35
+ source_annotations.each do |annotations|
36
+ alignment = TextAlignment::TextAlignment.new(annotations[:text], target_annotations[:text])
37
+
38
+ # alignment.block_alignments.each do |a|
39
+ # p {source:a[:source], target:a[:target]}
40
+ # puts "--"
41
+ # p a[:alignment] if a[:alignment].nil? || a[:alignment] == :empty
42
+ # puts "--"
43
+ # puts annotations[:text][a[:source][:begin] ... a[:source][:end]]
44
+ # puts "--"
45
+ # puts target_text[a[:target][:begin] ... a[:target][:end]]
46
+ # puts "======"
47
+ # end
48
+
49
+ if annotations.has_key?(:denotations) && !annotations[:denotations].empty?
50
+ ididx = {}
51
+ denotations = alignment.transform_hdenotations(annotations[:denotations])
52
+ denotations.each do |d|
53
+ reid = 'T' + (idnum_denotations += 1).to_s
54
+ ididx[d[:id]] = reid
55
+ d[:id] = reid
56
+ end
57
+ target_annotations[:denotations] = [] unless target_annotations.has_key? :denotations
58
+ target_annotations[:denotations] += denotations
59
+
60
+ if annotations.has_key?(:relations) && !annotations[:relations].empty?
61
+ target_annotations[:relations] = [] unless target_annotations.has_key? :relations
62
+ annotations[:relations].each do |r|
63
+ reid = 'R' + (idnum_relations += 1).to_s
64
+ ididx[r[:id]] = reid
65
+ target_annotations[:relations] << r.dup.merge({id:reid, subj:ididx[r[:subj]], obj:ididx[r[:obj]]})
66
+ end
67
+ end
68
+
69
+ if annotations.has_key?(:attributes) && !annotations[:attributes].empty?
70
+ target_annotations[:attributes] = [] unless target_annotations.has_key? :attributes
71
+ annotations[:attributes].each do |a|
72
+ reid = 'A' + (idnum_attributes += 1).to_s
73
+ ididx[a[:id]] = reid
74
+ target_annotations[:attributes] << a.dup.merge({id:reid, subj:ididx[a[:subj]]})
75
+ end
76
+ end
77
+
78
+ if annotations.has_key?(:modifications) && !annotations[:modifications].empty?
79
+ target_annotations[:modifications] = [] unless target_annotations.has_key? :modifications
80
+ annotations[:modifications].each do |m|
81
+ reid = 'M' + (idnum_modifications += 1).to_s
82
+ ididx[m[:id]] = reid
83
+ target_annotations[:modifications] << m.dup.merge({id:reid, obj:ididx[m[:obj]]})
84
+ end
85
+ end
86
+ end
87
+ end
88
+ target_annotations
89
+ end
90
+
91
+
7
92
  unless ARGV.length == 2
8
- warn "align_annotations target_annotations(.json) reference_annotations(.json)"
93
+ warn "align_annotations target_annotations(.json|.txt) reference_annotations(.json|.txt)"
9
94
  exit
10
95
  end
11
96
 
12
- anns1 = JSON.parse File.read(ARGV[0].strip), :symbolize_names => true
13
- anns2 = JSON.parse File.read(ARGV[1].strip), :symbolize_names => true
14
-
15
- str1 = anns1[:text]
16
- str2 = anns2[:text]
17
-
18
- denotations = anns1[:denotations]
19
-
20
- puts "[Alignment1]====="
21
- align = TextAlignment::TextAlignment.new(str1, str2, TextAlignment::MAPPINGS)
22
- puts TextAlignment::sdiff2cdiff(align.sdiff)
23
- puts
24
- puts "[Similarity]\n#{align.similarity}"
25
- puts
26
- puts '[Denotations original]'
27
- pp denotations
28
- puts
29
- puts '[Denotations transformed]'
30
- new_denotations = align.transform_hdenotations(denotations)
31
- pp new_denotations
32
- puts
33
- puts "[Alignment2 (downcased)]====="
34
- align = TextAlignment::TextAlignment.new(str1.downcase, str2.downcase, TextAlignment::MAPPINGS)
35
- puts TextAlignment::sdiff2cdiff(align.sdiff)
36
- puts
37
- puts "[Similarity]\n#{align.similarity}"
38
- puts
39
- puts '[Denotations original]'
40
- pp denotations
41
- puts
42
- puts '[Denotations transformed]'
43
- new_denotations = align.transform_hdenotations(denotations)
44
- pp new_denotations
45
- puts
46
- puts '[Annotations transformed]'
47
- anns2[:denotations] = new_denotations
48
- puts anns2.to_json
97
+ source_annotations = read_annotations(ARGV[0])
98
+ target_text = read_text(ARGV[1])
99
+
100
+ lost_annotations = []
101
+ target_annotations = if source_annotations.class == Array
102
+ align_mdoc(source_annotations, {text: target_text})
103
+ else
104
+ alignment = TextAlignment::TextAlignment.new(source_annotations[:text], target_text)
105
+
106
+ # alignment.block_alignments.each do |a|
107
+ # if a[:alignment].nil? || a[:alignment] == :empty
108
+ # # p [a[:source], a[:target]]
109
+ # # p a[:alignment]
110
+ # else
111
+ # p [a[:source], a[:target]]
112
+ # p a[:alignment].similarity
113
+ # puts "--"
114
+ # puts source_annotations[:text][a[:source][:begin] ... a[:source][:end]]
115
+ # puts "--"
116
+ # puts target_text[a[:target][:begin] ... a[:target][:end]]
117
+ # puts "======"
118
+ # end
119
+ # end
120
+ # exit
121
+
122
+ denotations = alignment.transform_hdenotations(source_annotations[:denotations])
123
+ lost_annotations += alignment.lost_annotations if alignment.lost_annotations
124
+
125
+ source_annotations.merge({text:target_text, denotations:denotations})
126
+ end
127
+
128
+ num_denotations_source, num_relations_source, num_attributes_source, num_modifications_source = if source_annotations.class == Array
129
+ num_denotations_source, num_relations_source, num_attributes_source, num_modifications_source = 0, 0, 0, 0
130
+ source_annotations.each do |annotations|
131
+ num_denotations_source += annotations[:denotations].nil? ? 0 : annotations[:denotations].length
132
+ num_relations_source += annotations[:relations].nil? ? 0 : annotations[:relations].length
133
+ num_attributes_source += annotations[:attributes].nil? ? 0 : annotations[:attributes].length
134
+ num_modifications_source += annotations[:modifications].nil? ? 0 : annotations[:modifications].length
135
+ end
136
+ [num_denotations_source, num_relations_source, num_attributes_source, num_modifications_source]
137
+ else
138
+ num_denotations_source = source_annotations[:denotations].nil? ? 0 : source_annotations[:denotations].length
139
+ num_relations_source = source_annotations[:relations].nil? ? 0 : source_annotations[:relations].length
140
+ num_attributes_source = source_annotations[:attributes].nil? ? 0 : source_annotations[:attributes].length
141
+ num_modifications_source = source_annotations[:modifications].nil? ? 0 : source_annotations[:modifications].length
142
+ [num_denotations_source, num_relations_source, num_attributes_source, num_modifications_source]
143
+ end
144
+
145
+ warn "[source]"
146
+ warn "denotations:\t#{num_denotations_source}"
147
+ # warn "relations:\t#{num_relations_source}"
148
+ # warn "attributes:\t#{num_attributes_source}"
149
+ # warn "modifications:\t#{num_modifications_source}"
150
+
151
+ warn "\n[target]"
152
+ warn "denotations:\t#{target_annotations[:denotations].nil? ? 0 : target_annotations[:denotations].length}"
153
+ # warn "relations:\t#{target_annotations[:relations].nil? ? 0 : target_annotations[:relations].length}"
154
+ # warn "attributes:\t#{target_annotations[:attributes].nil? ? 0 : target_annotations[:attributes].length}"
155
+ # warn "modifications:\t#{target_annotations[:modifications].nil? ? 0 : target_annotations[:modifications].length}"
156
+
157
+ if lost_annotations
158
+ warn "\n[lost annotations]"
159
+ warn "#{lost_annotations.length}"
160
+ end
161
+
162
+ puts target_annotations.to_json
163
+
164
+ # denotations = anns1[:denotations]
165
+
166
+ # puts "[Alignment1]====="
167
+ # align = TextAlignment::TextAlignment.new(str1, str2, TextAlignment::MAPPINGS)
168
+
169
+ # align.alignment.each do |a|
170
+ # p [a[:target][:begin], a[:target][:end], a[:source][:begin], a[:source][:end]]
171
+ # end
172
+
173
+ # puts TextAlignment::sdiff2cdiff(align.sdiff)
174
+ # puts
175
+ # puts "[Similarity]\n#{align.similarity}"
176
+ # puts
177
+ # puts '[Denotations original]'
178
+ # pp denotations
179
+ # puts
180
+ # puts '[Denotations transformed]'
181
+ # new_denotations = align.transform_hdenotations(denotations)
182
+ # pp new_denotations
183
+ # puts
184
+ # puts "[Alignment2 (downcased)]====="
185
+ # align = TextAlignment::TextAlignment.new(str1.downcase, str2.downcase, TextAlignment::MAPPINGS)
186
+ # puts TextAlignment::sdiff2cdiff(align.sdiff)
187
+ # puts
188
+ # puts "[Similarity]\n#{align.similarity}"
189
+ # puts
190
+ # puts '[Denotations original]'
191
+ # pp denotations
192
+ # puts
193
+ # puts '[Denotations transformed]'
194
+ # new_denotations = align.transform_hdenotations(denotations)
195
+ # pp new_denotations
196
+ # puts
197
+ # puts '[Annotations transformed]'
198
+ # anns2[:denotations] = new_denotations
199
+ # puts anns2.to_json
49
200
 
50
201
  # p align.common_elements
51
202
  # puts "---------------"
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env ruby
2
+ require 'string-similarity'
3
+
4
+ module TextAlignment; end unless defined? TextAlignment
5
+
6
+ TextAlignment::SIZE_NGRAM = 5 unless defined? TextAlignment::SIZE_NGRAM
7
+ TextAlignment::SIZE_WINDOW = 10 unless defined? TextAlignment::SIZE_WINDOW
8
+ TextAlignment::TEXT_SIMILARITY_TRESHOLD = 0.7 unless defined? TextAlignment::TEXT_SIMILARITY_TRESHOLD
9
+
10
+ class TextAlignment::AnchorFinder
11
+
12
+ def initialize(source_str, target_str, _size_ngram = nil, _size_window = nil)
13
+ @size_ngram = _size_ngram || TextAlignment::SIZE_NGRAM
14
+ @size_window = _size_window || TextAlignment::SIZE_WINDOW
15
+
16
+ @reverse = (target_str.length < source_str.length)
17
+
18
+ @s1, @s2 = if @reverse
19
+ [target_str.downcase, source_str.downcase]
20
+ else
21
+ [source_str.downcase, target_str.downcase]
22
+ end
23
+
24
+ # current position in s1
25
+ @beg_s1 = 0
26
+ end
27
+
28
+ def get_next_anchor
29
+ # find the position of an anchor ngram in s1 and s2
30
+ while @beg_s1 < (@s1.length - @size_ngram)
31
+ anchor = @s1[@beg_s1, @size_ngram]
32
+
33
+ search_position = 0
34
+ while @beg_s2 = @s2.index(anchor, search_position)
35
+ # if both the begining points are sufficiantly close to the end points of the last match
36
+ break if @end_s1_prev && (@beg_s1 - @end_s1_prev < 5) && (@beg_s2 - @end_s2_prev < 5)
37
+
38
+ left_window_s1, left_window_s2 = get_left_windows
39
+ break if left_window_s1 && text_similarity(left_window_s1, left_window_s2) > TextAlignment::TEXT_SIMILARITY_TRESHOLD
40
+
41
+ right_window_s1, right_window_s2 = get_right_windows
42
+ break if right_window_s2 && text_similarity(right_window_s1, left_window_s2) > TextAlignment::TEXT_SIMILARITY_TRESHOLD
43
+
44
+ search_position = @beg_s2 + 1
45
+ end
46
+
47
+ break unless @beg_s2.nil?
48
+
49
+ @beg_s1 += 1
50
+ end
51
+
52
+ return nil if @beg_s1 >= (@s1.length - @size_ngram)
53
+
54
+ # extend the block
55
+ b1 = @beg_s1
56
+ b2 = @beg_s2
57
+ while b1 > -1 && b2 > -1 && @s1[b1] == @s2[b2]
58
+ b1 -= 1; b2 -= 1
59
+ end
60
+ b1 += 1; b2 += 1
61
+
62
+ e1 = @beg_s1 + @size_ngram
63
+ e2 = @beg_s2 + @size_ngram
64
+ while @s1[e1] && @s1[e1] == @s2[e2]
65
+ e1 += 1; e2 += 1
66
+ end
67
+
68
+ @end_s1_prev = e1
69
+ @end_s2_prev = e2
70
+ @beg_s1 = e1
71
+
72
+ if @reverse
73
+ {source:{begin:b2 , end:e2}, target:{begin:b1, end:e1}}
74
+ else
75
+ {source:{begin:b1 , end:e1}, target:{begin:b2, end:e2}}
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def get_left_windows
82
+ return if @beg_s1 < @size_window || @beg_s2 < @size_window
83
+
84
+ window_s1 = ''
85
+ loc = @beg_s1 - 1
86
+ count = 0
87
+ while count < @size_window && loc >= 0
88
+ if @s1[loc] =~ /[0-9a-zA-Z]/
89
+ window_s1 += @s1[loc]
90
+ count += 1
91
+ end
92
+ loc -= 1
93
+ end
94
+
95
+ window_s2 = ''
96
+ loc = @beg_s2 - 1
97
+ count = 0
98
+ while count < @size_window && loc >= 0
99
+ if @s2[loc] =~ /[0-9a-zA-Z]/
100
+ window_s2 += @s2[loc]
101
+ count += 1
102
+ end
103
+ loc -= 1
104
+ end
105
+
106
+ [window_s1, window_s2]
107
+ end
108
+
109
+ def get_right_windows
110
+ return if (@beg_s1 + @size_ngram < (@s1.length - @size_window)) || (@beg_s2 + @size_ngram < (@s2.length - @size_window))
111
+
112
+ window_s1 = ''
113
+ loc = @beg_s1 + @size_ngram
114
+ len_s1 = @s1.length
115
+ count = 0
116
+ while count < @size_window && loc < len_s1
117
+ if @s1[loc] =~ /[0-9a-zA-Z]/
118
+ window_s1 += @s1[loc]
119
+ count += 1
120
+ end
121
+ loc += 1
122
+ end
123
+
124
+ window_s2 = ''
125
+ loc = @beg_s2 + @size_ngram
126
+ len_s2 = @s2.length
127
+ count = 0
128
+ while count < @size_window && loc < len_s2
129
+ if @s2[loc] =~ /[0-9a-zA-Z]/
130
+ window_s2 += @s2[loc]
131
+ count += 1
132
+ end
133
+ loc += 1
134
+ end
135
+
136
+ [window_s1, window_s2]
137
+ end
138
+
139
+ def text_similarity(str1, str2, ngram_order = 2)
140
+ String::Similarity.cosine(str1, str2, ngram:ngram_order)
141
+ end
142
+
143
+ end
@@ -4,72 +4,73 @@ require 'string-similarity'
4
4
  module TextAlignment; end unless defined? TextAlignment
5
5
 
6
6
  # approximate the location of str1 in str2
7
- module TextAlignment
8
- SIGNATURE_NGRAM = 5
9
- MIN_LENGTH_FOR_APPROXIMATION = 50
10
- BUFFER_RATE = 0.1
11
- TEXT_SIMILARITY_TRESHOLD = 0.8
12
- end
7
+ TextAlignment::SIGNATURE_NGRAM = 7 unless defined? TextAlignment::SIGNATURE_NGRAM
8
+ TextAlignment::MIN_LENGTH_FOR_APPROXIMATION = 50 unless defined? TextAlignment::MIN_LENGTH_FOR_APPROXIMATION
9
+ TextAlignment::BUFFER_RATE = 0.1 unless defined? TextAlignment::BUFFER_RATE
10
+ TextAlignment::TEXT_SIMILARITY_TRESHOLD = 0.7 unless defined? TextAlignment::TEXT_SIMILARITY_TRESHOLD
13
11
 
14
12
  class << TextAlignment
15
13
 
16
- # If finds an approximate region of str2 that contains str1
17
- def approximate_fit(str1, str2)
18
- raise ArgumentError, 'nil string' if str1.nil? || str2.nil?
19
- return 0, str2.length if str2.length < TextAlignment::MIN_LENGTH_FOR_APPROXIMATION
14
+ # If finds an approximate region of str2 that contains str1
15
+ def approximate_fit(str1, str2)
16
+ raise ArgumentError, 'nil string' if str1.nil? || str2.nil?
17
+ return 0, str2.length if str2.length < TextAlignment::MIN_LENGTH_FOR_APPROXIMATION
20
18
 
21
- ngram1 = (0 .. str1.length - TextAlignment::SIGNATURE_NGRAM).collect{|i| str1[i, TextAlignment::SIGNATURE_NGRAM]}
22
- ngram2 = (0 .. str2.length - TextAlignment::SIGNATURE_NGRAM).collect{|i| str2[i, TextAlignment::SIGNATURE_NGRAM]}
23
- ngram_shared = ngram1 & ngram2
19
+ ngram1 = (0 .. str1.length - TextAlignment::SIGNATURE_NGRAM).collect{|i| str1[i, TextAlignment::SIGNATURE_NGRAM]}
20
+ ngram2 = (0 .. str2.length - TextAlignment::SIGNATURE_NGRAM).collect{|i| str2[i, TextAlignment::SIGNATURE_NGRAM]}
21
+ ngram_shared = ngram1 & ngram2
24
22
 
25
- # If there is no shared n-gram found, it may mean there is no serious overlap between the two strings
26
- return nil, nil if ngram_shared.empty?
23
+ # If there is no shared n-gram found, it may mean there is no serious overlap between the two strings
24
+ return nil, nil if ngram_shared.empty?
27
25
 
28
- signature_ngrams = ngram_shared.select{|g| ngram2.count(g) == 1}
29
- return nil, nil if signature_ngrams.empty? #raise "no signature ngram"
26
+ signature_ngrams = ngram_shared.select{|g| ngram2.count(g) == 1}
27
+ return nil, nil if signature_ngrams.empty? #raise "no signature ngram"
30
28
 
31
- fit_begin, fit_end = nil, nil
32
- signature_ngrams.each do |signature_ngram|
33
- loc_signature_ngram_in_str1 = str1.index(signature_ngram)
34
- loc_signature_ngram_in_str2 = str2.index(signature_ngram)
29
+ cache = {}
30
+ fit_begin, fit_end = nil, nil
31
+ signature_ngrams.each do |signature_ngram|
32
+ loc_signature_ngram_in_str1 = str1.index(signature_ngram)
33
+ loc_signature_ngram_in_str2 = str2.index(signature_ngram)
35
34
 
36
- # approximate the beginning of the fit
37
- fit_begin = loc_signature_ngram_in_str2 - loc_signature_ngram_in_str1 - (loc_signature_ngram_in_str1 * TextAlignment::BUFFER_RATE).to_i
38
- fit_begin = 0 if fit_begin < 0
35
+ # approximate the beginning of the fit
36
+ fit_begin = loc_signature_ngram_in_str2 - loc_signature_ngram_in_str1 - (loc_signature_ngram_in_str1 * TextAlignment::BUFFER_RATE).to_i
37
+ fit_begin = 0 if fit_begin < 0
39
38
 
40
- # approximate the end of the fit
41
- offset_end = str1.length - loc_signature_ngram_in_str1
42
- fit_end = loc_signature_ngram_in_str2 + offset_end + (offset_end * TextAlignment::BUFFER_RATE).to_i
43
- fit_end = str2.length if fit_end > str2.length
39
+ # approximate the end of the fit
40
+ offset_end = str1.length - loc_signature_ngram_in_str1
41
+ fit_end = loc_signature_ngram_in_str2 + offset_end + (offset_end * TextAlignment::BUFFER_RATE).to_i
42
+ fit_end = str2.length if fit_end > str2.length
44
43
 
45
- text_similarity = text_similarity(str1, str2[fit_begin ... fit_end])
46
- break if text_similarity > TextAlignment::TEXT_SIMILARITY_TRESHOLD
47
- fit_begin, fit_end = nil, nil
48
- end
44
+ next if cache.has_key?("#{fit_begin}-#{fit_end}")
45
+ text_similarity = text_similarity(str1, str2[fit_begin ... fit_end])
46
+ cache["#{fit_begin}-#{fit_end}"] = text_similarity
49
47
 
50
- return nil, nil if fit_begin >= fit_end
51
- return fit_begin, fit_end
52
- end
48
+ break if text_similarity > TextAlignment::TEXT_SIMILARITY_TRESHOLD
49
+ fit_begin, fit_end = nil, nil
50
+ end
51
+ return fit_begin, fit_end if fit_begin && fit_end && fit_begin < fit_end
52
+ return nil, nil
53
+ end
53
54
 
54
- private
55
+ private
55
56
 
56
- def text_similarity(str1, str2, ngram_order = 3)
57
- _str1 = str1.delete(" \t\r\n")
58
- _str2 = str2.delete(" \t\r\n")
59
- String::Similarity.cosine(_str1, _str2, ngram:2)
60
- end
57
+ def text_similarity(str1, str2, ngram_order = 3)
58
+ _str1 = str1.delete(" \t\r\n")
59
+ _str2 = str2.delete(" \t\r\n")
60
+ String::Similarity.cosine(_str1, _str2, ngram:2)
61
+ end
61
62
 
62
63
  end
63
64
 
64
65
  if __FILE__ == $0
65
- require 'json'
66
+ require 'json'
66
67
 
67
- if ARGV.length == 2
68
- str1 = JSON.parse(File.read(ARGV[0]).strip)["text"]
69
- str2 = JSON.parse(File.read(ARGV[1]).strip)["text"]
68
+ if ARGV.length == 2
69
+ str1 = JSON.parse(File.read(ARGV[0]).strip)["text"]
70
+ str2 = JSON.parse(File.read(ARGV[1]).strip)["text"]
70
71
 
71
- loc = TextAlignment::approximate_fit(str1, str2)
72
- p loc
73
- puts str2[loc[0]...loc[1]]
74
- end
72
+ loc = TextAlignment::approximate_fit(str1, str2)
73
+ p loc
74
+ puts str2[loc[0]...loc[1]]
75
+ end
75
76
  end