MeCab ver.0.996の辞書ファイルの設定【Windows】

#1. はじめに
　この記事では、日本語形態素解析エンジン MeCab ver.0.996 における辞書ファイルの設定について記載しています。OSは、Windowsを前提としています。
　[MeCab: Yet Another Part-of-Speech and Morphological Analyzer](https://taku910.github.io/mecab/)には、何故かWindowsのインストールについては、自己解凍インストーラの話しか書いてありません。辞書ファイルの設定が何も記載されていません。

>Windows
>バイナリをインストールする場合は, 自己解凍インストーラ (mecab-X.X.exe) を実行してください. 辞書も同時にインストールされます。

　以下は、MeCab ver.0.996 のソースコードから読み取った手探りです。
　なお、以下のソリューションでデパッグすると、以下の動作がトレース可能です。

　https://github.com/k-ayaki/mecab-msvc

https://qiita.com/kenichiro_ayaki/items/f6518767b8aff0064f11


#2. MeCabの辞書ファイルについて
　MeCabの辞書ファイルは、以下のものです。これは、C:\Program Files (x86)\MeCab\dic\ipadic に格納されているものです。

| ファイル名  | 機能              |
|:-----------|:------------------|
| sys.dic    | システム辞書       |
| unk.dic    | 未知語辞書         |
| dicrc      | IPA辞書の定義ファイル |

　辞書の定義ファイルには、sys.dicとunk.dic のコストファクター、評価サイズ、文字コードなどが記載されています。これは書き換えたことはありません。

```:dicrc
;
; Configuration file of IPADIC
;
; $Id: dicrc,v 1.4 2006/04/08 06:41:36 taku-ku Exp $;
;
cost-factor = 800
bos-feature = BOS/EOS,*,*,*,*,*,*,*,*
eval-size = 8
unk-eval-size = 4
config-charset = SHIFT-JIS

; yomi
node-format-yomi = %pS%f[7]
unk-format-yomi = %M
eos-format-yomi  = \n

; simple
node-format-simple = %m\t%F-[0,1,2,3]\n
eos-format-simple  = EOS\n

; ChaSen
node-format-chasen = %m\t%f[7]\t%f[6]\t%F-[0,1,2,3]\t%f[4]\t%f[5]\n
unk-format-chasen  = %m\t%m\t%m\t%F-[0,1,2,3]\t\t\n
eos-format-chasen  = EOS\n

; ChaSen (include spaces)
node-format-chasen2 = %M\t%f[7]\t%f[6]\t%F-[0,1,2,3]\t%f[4]\t%f[5]\n
unk-format-chasen2  = %M\t%m\t%m\t%F-[0,1,2,3]\t\t\n
eos-format-chasen2  = EOS\n
```

　この辞書ファイルの場所を指定しているのが、C:\Program Files (x86)\MeCab\etc\mecabrc です。
　dicdir に、ipa辞書のパスが格納されています。なお、$(rcpath)は、当該ファイルのパスのことです。

```:mecabrc
;
; Configuration file of MeCab
;
; $Id: mecabrc.in,v 1.3 2006/05/29 15:36:08 taku-ku Exp $;
;
dicdir =  $(rcpath)\..\dic\ipadic

; userdic = /home/foo/bar/user.dic

; output-format-type = wakati
; input-buffer-size = 8192

; node-format = %m\n
; bos-format = %S\n
; eos-format = EOS\n
```
　MeCabのライブラリである libmecab.dll は、mecabrcを読み込んでipa辞書ファイルを特定したのち、sys.dic, unk.dic, dicrcなどを読み込んでいます。

#3. libmecab.dll による mecabrc パスの特定方法

#3.1. 環境変数による特定
　環境変数 HOMEがあれば、mecabrcのパスとして読み取ります。
　次に環境変数MECABRCがあれば、mecabrcのパスとして読み取ります。

　なお、コンパイル時のマクロ HAVE_GETENV を定義しないとき、libmecab.dll は環境変数を読み取らなくなります。

```cpp:utils.cpp
#ifdef HAVE_GETENV
  if (rcfile.empty()) {
    const char *homedir = getenv("HOME");
    if (homedir) {
      const std::string s = MeCab::create_filename(std::string(homedir),
                                                   ".mecabrc");
      std::ifstream ifs(WPATH(s.c_str()));
      if (ifs) {
        rcfile = s;
      }
    }
  }

  if (rcfile.empty()) {
    const char *rcenv = getenv("MECABRC");
    if (rcenv) {
      rcfile = rcenv;
    }
  }
#endif

#if defined (HAVE_GETENV) && defined(_WIN32) && !defined(__CYGWIN__)
  if (rcfile.empty()) {
    scoped_fixed_array<wchar_t, BUF_SIZE> buf;
    const DWORD len = ::GetEnvironmentVariableW(L"MECABRC",
                                                buf.get(),
                                                buf.size());
    if (len < buf.size() && len > 0) {
      rcfile = WideToUtf8(buf.get());
    }
  }
#endif
```
#3.2. レジストリによる特定（Windows固有）
　環境変数が定義されておらず、且つ、レジストリ HKEY_LOCAL_MACHINE\software\mecab に、mecabrcキーがあれば、その値を mecabrcのパスとして読み取ります。
　レジストリ HKEY_CURRENT_USER\software\mecab に、mecabrcキーがあれば、その値を mecabrcのパスとして読み取ります。

　なお、Windows版のMeCab ver.0.996 をインストールしたときには、レジストリ HKEY_CURRENT_USER\software\mecabに、mecabrcキーが書き込まれます。

```cpp:utils.cpp
#if defined(_WIN32) && !defined(__CYGWIN__)
  HKEY hKey;
  scoped_fixed_array<wchar_t, BUF_SIZE> v;
  DWORD vt;
  DWORD size = v.size() * sizeof(v[0]);

  if (rcfile.empty()) {
    ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"software\\mecab", 0, KEY_READ, &hKey);
    ::RegQueryValueExW(hKey, L"mecabrc", 0, &vt,
                       reinterpret_cast<BYTE *>(v.get()), &size);
    ::RegCloseKey(hKey);
    if (vt == REG_SZ) {
      rcfile = WideToUtf8(v.get());
    }
  }

  if (rcfile.empty()) {
    ::RegOpenKeyExW(HKEY_CURRENT_USER, L"software\\mecab", 0, KEY_READ, &hKey);
    ::RegQueryValueExW(hKey, L"mecabrc", 0, &vt,
                       reinterpret_cast<BYTE *>(v.get()), &size);
    ::RegCloseKey(hKey);
    if (vt == REG_SZ) {
      rcfile = WideToUtf8(v.get());
    }
  }
```

#3.3. libmecab.dllのパスによる特定（Windows固有）
　環境変数とレジストリが定義されていないならば、libmecab.dllのパスを取得して、mecabrcのパスとします。

```cpp:utils.cpp（Windows固有）
  if (rcfile.empty()) {
    vt = ::GetModuleFileNameW(DllInstance, v.get(), size);
    if (vt != 0) {
      scoped_fixed_array<wchar_t, _MAX_DRIVE> drive;
      scoped_fixed_array<wchar_t, _MAX_DIR> dir;
      _wsplitpath(v.get(), drive.get(), dir.get(), NULL, NULL);
      const std::wstring path =
          std::wstring(drive.get()) + std::wstring(dir.get()) + L"mecabrc";
      if (::GetFileAttributesW(path.c_str()) != -1) {
        rcfile = WideToUtf8(path);
      }
    }
  }
```

#3.4. マクロ定義したパスによる特定
　マクロ MECAB_DEFAULT_RC で定義したパスを、mecabrcのパスとします。

```cpp:utils.cpp
  if (rcfile.empty()) {
    rcfile = MECAB_DEFAULT_RC;
  }
```

#3.5. 特定したmecabrcパスの読み込み

　特定したmecabrcパスに基づき、dicdir（辞書パス）を読み込みます。

```cpp:utils.cpp
  if (!param->load(rcfile.c_str())) {
    return false;
  }
```
#3.6. dicdir（辞書パス）の決定

　特定したmecabrcパスに基づき、dicdir（辞書パス）を読み込みます。

```cpp:utils.cpp
  std::string dicdir = param->get<std::string>("dicdir");
  if (dicdir.empty()) {
    dicdir = ".";  // current
  }
  remove_filename(&rcfile);
  replace_string(&dicdir, "$(rcpath)", rcfile);
  param->set<std::string>("dicdir", dicdir, true);
  dicdir = create_filename(dicdir, DICRC);

  if (!param->load(dicdir.c_str())) {
    return false;
  }

  return true;
```