curlkun commited on
Commit
f8a70f4
·
verified ·
1 Parent(s): 0b3d0bb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +107 -19
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import re
2
 
3
  HYPHENS = set(['\u002D','\uFF0D','\u2010','\u2011','\u2013','\u2014'])
@@ -98,22 +99,109 @@ def build_active_text_and_map(original, flags):
98
  index_map = [i for i, _ in active_chars]
99
  return active_text, index_map
100
 
101
- src25 = "abc(def)123アイウ〔ghi〕fi⼀zero-test \nabc\n(def)123アイウ〔ghi〕fi⼀zero-test \nabc"
102
- dst25 = "xyz(www)789カキクケコ〔jkl〕fl⼆one+test \nxyz\n(www)789カキクケコ〔jkl〕fl⼆one+test \nxyz"
103
- settings25 = {
104
- 'ignore_alpha': True, 'ignore_digit': True, 'ignore_kana': True, 'ignore_yakumono': True,
105
- 'ignore_ligature': True, 'ignore_kangxi': True, 'ignore_brackets_with_content': True,
106
- 'ignore_kanjinum': True, 'ignore_hyphen': True, 'ignore_kumimoji': True,
107
- 'ignore_space': True, 'ignore_newline': True
108
- }
109
-
110
- src_flags = build_active_flags(src25, settings25)
111
- dst_flags = build_active_flags(dst25, settings25)
112
- src_active, _ = build_active_text_and_map(src25, src_flags)
113
- dst_active, _ = build_active_text_and_map(dst25, dst_flags)
114
- print(f"src_active: '{src_active}'")
115
- print(f"dst_active: '{dst_active}'")
116
- src_removed = ''.join(ch for i, ch in enumerate(src25) if not src_flags[i])
117
- dst_removed = ''.join(ch for i, ch in enumerate(dst25) if not dst_flags[i])
118
- print(f"src_removed: '{src_removed}'")
119
- print(f"dst_removed: '{dst_removed}'")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import difflib
2
  import re
3
 
4
  HYPHENS = set(['\u002D','\uFF0D','\u2010','\u2011','\u2013','\u2014'])
 
99
  index_map = [i for i, _ in active_chars]
100
  return active_text, index_map
101
 
102
+ def build_removed_text_and_map(original, flags):
103
+ removed_chars = [(i, ch) for i, ch in enumerate(original) if not flags[i]]
104
+ removed_text = ''.join(ch for _, ch in removed_chars)
105
+ index_map = [i for i, _ in removed_chars]
106
+ return removed_text, index_map
107
+
108
+ def merge_ranges(ranges):
109
+ merged = []
110
+ for s, e in sorted(ranges):
111
+ if s >= e: continue
112
+ if merged and s <= merged[-1][1]:
113
+ merged[-1] = (merged[-1][0], max(merged[-1][1], e))
114
+ else:
115
+ merged.append([s, e])
116
+ return [(s, e) for s, e in merged]
117
+
118
+ def compare(src, dst, settings):
119
+ src_flags = build_active_flags(src, settings)
120
+ dst_flags = build_active_flags(dst, settings)
121
+
122
+ src_active, src_map = build_active_text_and_map(src, src_flags)
123
+ dst_active, dst_map = build_active_text_and_map(dst, dst_flags)
124
+
125
+ src_diff_ranges = []
126
+ dst_diff_ranges = []
127
+
128
+ # 有効文字の比較(両方空なら差分なし)
129
+ if src_active != '' or dst_active != '':
130
+ matcher = difflib.SequenceMatcher(None, src_active, dst_active, autojunk=False)
131
+ for tag, i1, i2, j1, j2 in matcher.get_opcodes():
132
+ if tag in ('replace', 'delete') and i2 > i1:
133
+ src_diff_ranges.append((src_map[i1], src_map[i2-1] + 1))
134
+ if tag in ('replace', 'insert') and j2 > j1:
135
+ dst_diff_ranges.append((dst_map[j1], dst_map[j2-1] + 1))
136
+
137
+ # 除去文字の比較
138
+ src_removed, src_removed_map = build_removed_text_and_map(src, src_flags)
139
+ dst_removed, dst_removed_map = build_removed_text_and_map(dst, dst_flags)
140
+
141
+ if src_removed != dst_removed and (src_active != '' or dst_active != ''):
142
+ # 除去文字同士をdifflibで比較
143
+ rm_matcher = difflib.SequenceMatcher(None, src_removed, dst_removed, autojunk=False)
144
+ for tag, i1, i2, j1, j2 in rm_matcher.get_opcodes():
145
+ if tag in ('replace', 'delete') and i2 > i1:
146
+ src_diff_ranges.append((src_removed_map[i1], src_removed_map[i2-1] + 1))
147
+ if tag in ('replace', 'insert') and j2 > j1:
148
+ dst_diff_ranges.append((dst_removed_map[j1], dst_removed_map[j2-1] + 1))
149
+
150
+ return merge_ranges(src_diff_ranges), merge_ranges(dst_diff_ranges)
151
+
152
+ def apply_highlight(text, ranges):
153
+ result = []
154
+ prev = 0
155
+ for s, e in ranges:
156
+ result.append(text[prev:s])
157
+ result.append('"' + text[s:e] + '"')
158
+ prev = e
159
+ result.append(text[prev:])
160
+ return ''.join(result)
161
+
162
+ test_cases = [
163
+ ("TEST-01", "吾輩は猫である。名前はまだない。", "吾輩は猫である。名前はまだない。", {}),
164
+ ("TEST-02", "吾輩は猫である。", "吾輩は犬である。", {}),
165
+ ("TEST-03", "abcdef", "abcXYZdef", {}),
166
+ ("TEST-04", "abcXYZdef", "abcdef", {}),
167
+ ("TEST-08", "吾輩は、猫である。「名前」は(まだ)ない!", "吾輩は猫である名前はまだない",
168
+ {'ignore_yakumono': True}),
169
+ ("TEST-09", "(abc)テスト", "abcテスト",
170
+ {'ignore_yakumono': True}),
171
+ ("TEST-19", "abc-def-ghi‐jkl‑mno–pqr—stu", "abcdefghijklmnopqrstu",
172
+ {'ignore_hyphen': True}),
173
+ ("TEST-21", "abc def ghi", "abcdefghi",
174
+ {'ignore_space': True}),
175
+ ("TEST-22", "abc\ndef\nghi", "abcdefghi",
176
+ {'ignore_newline': True}),
177
+ ("TEST-23", "吾輩は(猫)である。名前は【まだ】ない!", "吾輩はである。名前はない!",
178
+ {'ignore_yakumono': True, 'ignore_brackets_with_content': True}),
179
+ ("TEST-25",
180
+ "abc(def)123アイウ〔ghi〕fi⼀zero-test \nabc\n(def)123アイウ〔ghi〕fi⼀zero-test \nabc",
181
+ "xyz(www)789カキクケコ〔jkl〕fl⼆one+test \nxyz\n(www)789カキクケコ〔jkl〕fl⼆one+test \nxyz",
182
+ {'ignore_alpha': True, 'ignore_digit': True, 'ignore_kana': True, 'ignore_yakumono': True,
183
+ 'ignore_ligature': True, 'ignore_kangxi': True, 'ignore_brackets_with_content': True,
184
+ 'ignore_kanjinum': True, 'ignore_hyphen': True, 'ignore_kumimoji': True,
185
+ 'ignore_space': True, 'ignore_newline': True}),
186
+ ("TEST-30", "abc def", "abcdef", {}),
187
+ ("TEST-35", "abcXYZdef", "abcdef", {}),
188
+ ("TEST-37", "abc-def-ghi‐jkl‑mno–pqr—stu", "abcdefghijklmnopqrstu",
189
+ {'ignore_hyphen': True}),
190
+ # 比較元と比較先を入れ替えたケース
191
+ ("TEST-08-Rev", "吾輩は猫である名前はまだない", "吾輩は、猫である。「名前」は(まだ)ない!",
192
+ {'ignore_yakumono': True}),
193
+ ("TEST-19-Rev", "abcdefghijklmnopqrstu", "abc-def-ghi‐jkl‑mno–pqr—stu",
194
+ {'ignore_hyphen': True}),
195
+ ("TEST-21-Rev", "abcdefghi", "abc def ghi",
196
+ {'ignore_space': True}),
197
+ ("TEST-22-Rev", "abcdefghi", "abc\ndef\nghi",
198
+ {'ignore_newline': True}),
199
+ ("TEST-23-Rev", "吾輩はである。名前はない!", "吾輩は(猫)である。名前は【まだ】ない!",
200
+ {'ignore_yakumono': True, 'ignore_brackets_with_content': True}),
201
+ ]
202
+
203
+ for name, src, dst, settings in test_cases:
204
+ src_ranges, dst_ranges = compare(src, dst, settings)
205
+ print(f"\n=== {name} ===")
206
+ print(f" 比較元: {apply_highlight(src, src_ranges)}")
207
+ print(f" 比較先: {apply_highlight(dst, dst_ranges)}")