二つの文字列の共通部分@Python

今更…。

自力でやると結構煩雑。頭いたくなるよ。

前から近いニーズのことやってた気がしてたんだけど、意外と必要じゃなかったみたい。改めて考えてみたら、Python の場合は difflib のおかげで一撃なのであった:

 1 # -*- coding: utf-8 -*-
 2 from __future__ import unicode_literals
 3 
 4 from difflib import SequenceMatcher
 5 
 6 
 7 def common_seq(lhs, rhs):
 8     """
 9     >>> print(common_seq("ABChhh", "XYZkkk"))
10     <BLANKLINE>
11     >>> print(common_seq("ABChhh", "ABCkkk"))
12     ABC
13     >>> print(common_seq("zzABChhh", "ABCkkk"))
14     ABC
15     >>> print(common_seq("ABChhh", "yyABCkkk"))
16     ABC
17     >>> print(common_seq("zzABChhh", "yyABCkkk"))
18     ABC
19     >>> print(common_seq("uvwxABChhh", "yzABCkkk"))
20     ABC
21     >>> print(common_seq("uvwABChhh", "xyzABCkkk"))
22     ABC
23     """
24     sm = SequenceMatcher(a=lhs, b=rhs)
25     first_match = sm.get_matching_blocks()[0]
26     span = slice(first_match.a, first_match.a + first_match.size)
27     return lhs[span]
28 
29 
30 if __name__ == '__main__':
31     import doctest
32     doctest.testmod()

first_match と言ってることからわかると思うけど、一致部分全部を拾い上げることも出来る。

なお、difflib なので、例えば空白の無視とかそういったことも出来る、けど今回はやらない。あと difflib が文字列前提なので、文字列以外に適用したくなったらどうしようかと思うんだけど、今はまぁいいや。


追記。

つーか「一致部分全部を拾い上げる」方を標準に考えたほうが便利よね:

 1 # -*- coding: utf-8 -*-
 2 from __future__ import unicode_literals
 3 
 4 from difflib import SequenceMatcher
 5 
 6 
 7 def common_seq(lhs, rhs):
 8     r"""
 9     >>> print("\n".join(common_seq("ABChhh", "XYZkkk")))
10     <BLANKLINE>
11     >>> print("\n".join(common_seq("ABChhh", "ABCkkk")))
12     ABC
13     >>> print("\n".join(common_seq("zzABChhh", "ABCkkk")))
14     ABC
15     >>> print("\n".join(common_seq("ABChhh", "yyABCkkk")))
16     ABC
17     >>> print("\n".join(common_seq("zzABChhh", "yyABCkkk")))
18     ABC
19     >>> print("\n".join(common_seq("uvwxABChhh", "yzABCkkk")))
20     ABC
21     >>> print("\n".join(common_seq("uvwABChhh", "xyzABCkkk")))
22     ABC
23     """
24     sm = SequenceMatcher(a=lhs, b=rhs)
25     return [lhs[slice(m.a, m.a + m.size)]
26             for m in sm.get_matching_blocks() if m.size]
27 
28 
29 if __name__ == '__main__':
30     import doctest
31     doctest.testmod()