# -*- encoding: utf-8 -*-
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import distutils.spawn
import os
import sys
import unittest
import six
from pyknp.utils.analyzer import Analyzer
from .mlist import MList
from .morpheme import JUMAN_FORMAT
[docs]class Juman(object):
"""形態素解析器 JUMAN を Python から利用するためのモジュール
Args:
command (str): JUMANの実行コマンド
option (str): JUMAN解析オプション (ラティス形式 -s, ビーム幅 --beam <int>)
rcfile (str): JUMAN設定ファイルへのパス
pattern (str): JUMAN出力の終端記号
jumanpp (bool): JUMAN++を用いるかJUMANを用いるか。commandを指定した場合は無視される。
"""
[docs] def __init__(self,
command='jumanpp',
server=None,
port=32000,
timeout=30,
option='',
rcfile='',
ignorepattern='',
pattern=r'^EOS$',
jumanpp=True,
multithreading=False,
):
if jumanpp or command != 'jumanpp':
self.command = command
self.options = option.split()
else:
self.command = 'juman'
self.options = option.split() + ['-e2', '-B']
self.server = server
self.port = port
self.timeout = timeout
self.rcfile = rcfile
self.ignorepattern = ignorepattern
self.pattern = pattern
if server is not None:
self.analyzer = Analyzer(backend='socket', timeout=timeout, server=server, port=port,
socket_option='RUN -e2\n')
else:
cmds = [self.command] + self.options
if self.rcfile:
cmds += ['-r', self.rcfile]
self.analyzer = Analyzer(backend='subprocess', multithreading=multithreading, timeout=timeout, command=cmds)
if self.rcfile and not os.path.isfile(os.path.expanduser(self.rcfile)):
raise Exception("Can't read rcfile (%s)!" % self.rcfile)
if distutils.spawn.find_executable(self.command) is None:
raise Exception("Can't find JUMAN command: %s" % self.command)
[docs] def juman_lines(self, input_str):
""" 入力文字列に対して形態素解析を行い、そのJuman出力結果を返す
Args:
input_str (str): 文を表す文字列
Returns:
str: Juman出力結果
"""
if '\n' in input_str:
input_str = input_str.replace('\n', '')
print('Analysis is done ignoring "\\n".', file=sys.stderr)
return self.analyzer.query(input_str, pattern=self.pattern)
[docs] def juman(self, input_str, juman_format=JUMAN_FORMAT.DEFAULT):
""" analysis関数と同じ """
assert isinstance(input_str, six.text_type)
result = MList(self.juman_lines(input_str), juman_format)
return result
[docs] def analysis(self, input_str, juman_format=JUMAN_FORMAT.DEFAULT):
""" 入力文字列に対して形態素解析し、その結果を MList オブジェクトとして返す
Args:
input_str (str): 文を表す文字列
juman_format (JUMAN_FORMAT): Jumanのlattice出力形式
Returns:
MList: 形態素列オブジェクト
"""
return self.juman(input_str, juman_format)
[docs] def result(self, input_str, juman_format=JUMAN_FORMAT.DEFAULT):
""" Juman出力結果に対して、その結果を MList オブジェクトとして返す
Args:
input_str (str): Juman出力結果
juman_format (JUMAN_FORMAT): Jumanのlattice出力形式
Returns:
MList: 形態素列オブジェクト
"""
return MList(input_str, juman_format)
class JumanTest(unittest.TestCase):
def setUp(self):
self.jumanpp = Juman()
self.juman = Juman(jumanpp=False)
# JUMANPP
def test_normal_jumanpp(self):
test_str = "この文を解析してください。"
result = self.jumanpp.analysis(test_str)
self.assertEqual(len(result), 7)
self.assertEqual(''.join(mrph.midasi for mrph in result), test_str)
self.assertGreaterEqual(len(result.spec().split("\n")), 7)
def test_nominalization_jumanpp(self):
test_str = "音の響きを感じる。"
result = self.jumanpp.analysis(test_str)
self.assertEqual(len(result), 6)
self.assertEqual(''.join(mrph.midasi for mrph in result), test_str)
self.assertGreaterEqual(len(result.spec().split("\n")), 6)
self.assertEqual(result[2].midasi, "響き")
self.assertEqual(result[2].hinsi, "名詞")
def test_whitespace_jumanpp(self):
test_str = "半角 スペース"
result = self.jumanpp.analysis(test_str)
self.assertEqual(len(result), 3)
self.assertEqual((result[1].bunrui == '空白'), True)
self.assertEqual(''.join(mrph.midasi for mrph in result), test_str.replace(" ", "\ "))
self.assertGreaterEqual(len(result.spec().split("\n")), 3)
def test_eos(self):
test_str = "エネルギーを素敵にENEOS"
result = self.jumanpp.analysis(test_str)
self.assertEqual(''.join(mrph.midasi for mrph in result), test_str)
def test_eos2(self):
test_str = "Canon EOS 80D買った"
result = self.jumanpp.analysis(test_str)
self.assertEqual(''.join(mrph.midasi for mrph in result), test_str.replace(" ", "\ "))
def test_dquo(self):
test_str = "\"最高\"の気分"
result = self.jumanpp.analysis(test_str)
self.assertEqual(''.join(mrph.midasi for mrph in result), test_str)
def test_escape(self):
test_str = "<tag>\\エス\'ケープ"
result = self.jumanpp.analysis(test_str)
self.assertEqual(''.join(mrph.midasi for mrph in result), test_str)
def test_cr(self):
test_str = "これは\rどう"
result = self.jumanpp.analysis(test_str)
self.assertEqual(''.join(mrph.midasi for mrph in result), test_str)
# JUMAN
def test_normal_juman(self):
test_str = "この文を解析してください。"
result = self.juman.analysis(test_str)
self.assertEqual(len(result), 7)
self.assertEqual(''.join(mrph.midasi for mrph in result), test_str)
self.assertGreaterEqual(len(result.spec().split("\n")), 7)
def test_whitespace_juman(self):
test_str = "半角 スペース"
result = self.juman.analysis(test_str)
self.assertEqual(len(result), 4) # 半|角|\ |スペース
self.assertEqual((result[2].bunrui == '空白'), True)
self.assertEqual(''.join(mrph.midasi for mrph in result), test_str.replace(" ", "\ "))
self.assertGreaterEqual(len(result.spec().split("\n")), 4)
if __name__ == '__main__':
unittest.main()