【kuromoji】Android（Kotlin）でスタンドアロンで形態素解析をする方法

# この記事を書いた経緯
Andoridで形態素解析をしたくて色々と調べていたのですが、

- 情報が少ない
- あっても情報が古くて、そのままだと動かない

という状況だったので、このような記事を書きました。

# 概要
Android端末でスタンドアロンで形態素解析をする方法を紹介します。
形態素解析には[kuromoji](https://www.atilika.com/ja/kuromoji/)というオープンソース日本語形態素解析エンジンを使います。

# 形態素解析とは
[Wikipedia](https://ja.wikipedia.org/wiki/%E5%BD%A2%E6%85%8B%E7%B4%A0%E8%A7%A3%E6%9E%90)では以下のような説明がなされています。
> 形態素解析（けいたいそかいせき、Morphological Analysis）とは、文法的な情報の注記の無い自然言語のテキストデータ（文）から、対象言語の文法や、辞書と呼ばれる単語の品詞等の情報にもとづき、形態素（Morpheme, おおまかにいえば、言語で意味を持つ最小単位）の列に分割し、それぞれの形態素の品詞等を判別する作業である。

例えば、`今日はかつ丼を食べた`という文であれば、文を次のように分割します。

```txt
今日 / は / かつ丼 / を / 食べた
```

その上で、それぞれの単語の品詞などを求めます。

| 単語 | 品詞 |
|---|---|
|今日|名詞|
|は|助詞|
|かつ丼|名詞|
|を|助詞|
|食べ|動詞|
|た|助動詞|

[Web茶まめ](https://chamame.ninjal.ac.jp/)というサイトで手軽に形態素解析ができるので、色々な文を形態素解析してみると理解しやすいかもしれません。

# 形態素解析するための手順

## 使用するkuromoji
今回は次のkuromojiを使用します。

https://mvnrepository.com/artifact/com.atilika.kuromoji/kuromoji-ipadic/0.9.0

## 1. 初期設定

### 任意の設定
説明を簡略化するために、初期設定は以下のようにしました。この設定は任意であるので、別の設定をしても形態素解析はできます。

- テンプレートは`Empty Activity`を選択

### 必須の設定
kuromojiを使うためには、以下の設定が必要です。この設定をしないとビルドしたときにエラーが出ます。

- Minimum SDKを`26`以上に設定

## 2. app/build.gradle
今回はkuromojiを使うので、`dependencies { }` の中に以下を記載します。

```gradle: app/build.gradle
implementation 'com.atilika.kuromoji:kuromoji-ipadic:0.9.0'
```

このままSync Nowをしてビルドすると、次のようなエラーが出ます。

```txt
2 files found with path 'META-INF/CONTRIBUTORS.md'.
Adding a packagingOptions block may help, please refer to https://developer.android.com/reference/tools/gradle-api/7.4/com/android/build/api/dsl/ResourcesPackagingOptions
for more information
```

そのため、`android { }` の中に以下を記載します。

```gradle: app/build.gradle
packagingOptions {
    resources.excludes.add("META-INF/*")
}
```

この状態でSync Nowをしてビルドすると、今度は成功するはずです。

## 3. activity_main.xml
今回はTextViewに形態素解析した結果を表示します。そのため、以下のようにactivity_main.xmlに`id`が`text_view`であるTextViewを記載してください。

```xml: activity_main.xml
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""
        android:id="@+id/text_view"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

```

## 4. MainActivity.kt
MainActivityは以下のようにします。
`package`の部分はご自身で決めたパッケージ名にしてください。
下記のコードでは、kuromojiのトークナイザのインスタンスを生成し、`今日はかつ丼を食べた`という結果を文を形態素解析し、結果（単語とその品詞）をTextViewにセットしています。

```kotlin: MainActivity.kt
package com.example.ktkuromojiipadic

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import com.atilika.kuromoji.ipadic.Tokenizer

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val inputText = "今日はかつ丼を食べた"
        val tokenizer = Tokenizer()
        val tokens = tokenizer.tokenize(inputText)
        val textView = findViewById<TextView>(R.id.text_view)

        var outputText = ""
        for (token in tokens) {
            outputText += token.surface + ":" + token.partOfSpeechLevel1 + "\n"
        }

        textView.text = outputText
    }
}
```

## 5. 実行結果
実行結果は以下のようになりました。
`かつ丼`以外は正常に形態素解析ができたことが分かります。`かつ丼`はおそらくデフォルトの辞書に登録されていなかったため、上手く形態素解析がされなかったのだと思います。この後の章で、辞書に`かつ丼`を追加することで、この問題を解決します。

![形態素解析_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014686/c2c5aa66-8171-cd78-01cf-2b9c98b79d48.png)

# 辞書に新たな単語を登録するための手順
## 目標
先ほど上手く形態素解析ができなかった`かつ丼`を辞書に追加することで、正常に形態素解析ができるようにしていきたいと思います。

## 1. ユーザー辞書の用意

### テキストファイルの作成
まず、Assetsフォルダを用意します。作り方を以下のような記事を参考にしてください。

https://knowledge.moshimore.jp/android_assets_folder/

つぎに、作成したAssetsフォルダの中に、`userdictionary.txt`という名前のファイルを作ります。ファイルの名前はなんでもいいのですが、今回はこの名前でいきます。

### ユーザー辞書の作成
ファイルができたら、`かつ丼`を辞書に追加していきます。辞書の形式は以下の記事を参考にしました。

http://www.mwsoft.jp/programming/lucene/kuromoji.html

この記事によると辞書の形式は、`単語, 分割後の単語の形, 読み方, 品詞`となるそうです。

品詞の種類は以下の記事を参考にしました。

https://hayashibe.jp/tr/mecab/dictionary/ipadic

今回は`かつ丼`を辞書に追加したいので、`userdictionary.txt`に以下のように書き込みます。

```text: userdictionary.txt
かつ丼,かつ丼,カツドン,名詞
```

## 2. ユーザー辞書の利用
ユーザー辞書を読み込むために、前回のMainActivityを以下のように変更します。

```kotlin: MainActivity.kt
package com.example.ktkuromojiipadic

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import com.atilika.kuromoji.ipadic.Tokenizer

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val inputText = "今日はかつ丼を食べた"

        // 変更点 開始
        val assetManager = resources.assets
        val file = assetManager.open("userdictionary.txt")
        val tokenizer = Tokenizer.Builder().userDictionary(file).build<Tokenizer>()
        // 変更点 終了

        val tokens = tokenizer.tokenize(inputText)
        val textView = findViewById<TextView>(R.id.text_view)

        var outputText = ""
        for (token in tokens) {
            outputText += token.surface + ":" + token.partOfSpeechLevel1 + "\n"
        }

        textView.text = outputText
    }
}
```

## 3. 実行結果
実行結果は以下のようになりました。
`かつ丼`が期待通りに形態素解析されていることが分かります。

![形態素解析_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014686/282de194-2b38-b341-c63b-cbd4e5b33119.png)