くりーむわーかー

プログラムとか。作ってて ・試しててハマった事など。誰かのお役に立てば幸いかと。 その他、いろいろエトセトラ。。。

2017年10月

C# NMecabでユーザ辞書を使う

最近、またMecab使ってるんですが、Mecabはそのまま使うと凄く細かく分解されちゃって結構使いにくい。形態素解析したい文章である程度の単語のまとまりのままにしたい場合はユーザー辞書作るのが手っ取り早い。

C#でNMecab使う場合を想定。NMecabはNugetでインストール。

辞書作るときは、本家のサイトからWindows用をダウンロードしてインスト。

そしたら↓の感じのCSVで登録した単語の一覧を作る。例えば「chacha.csv」っていうファイル名で保存した場合。

ジャスミン茶,,,10,名詞,一般,*,*,*,*,独自辞書,ヨミ,ハツオン
ウーロン茶,,,10,名詞,一般,*,*,*,*,独自辞書,ヨミ,ハツオン

フォーマットは↓の感じらしい。自分は原型のところに分かりやすいように自分の辞書っていうのがわかる文字を入れてる。

表層形,左文脈ID,右文脈ID,コスト,品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用形,活用型,原形,読み,発音

そしたら、↓コマンドでコンパイルして辞書ファイルを作る。フォルダはインストールしたフォルダを適当に。

cd C:\Program Files (x86)\MeCab\bin
mecab-dict-index -d "C:\Program Files (x86)\MeCab\dic\ipadic" -u "C:\Program Files (x86)\MeCab\dic\userdic\chacha.dic" -f shift-jis -t utf-8 "C:\Program Files (x86)\MeCab\dic\userdic\chacha.csv"

オプションとかは「mecab-dict-index -h」でヘルプ見た方が早い。

で、読み込みのCSVはSJISでいいと思うんだけど、dicファイルの方はUTF8指定しておかないと、プログラム側で読み込むときにエラーになる。。。あと、CSVファイルの最後は改行入れない方がいい。

dicファイルが出来たらプログラム側の適当なフォルダに入れて、↓の感じでMeCabParamを作る。

MeCabParam mPara = new MeCabParam();
//まず、システム辞書があるフォルダの指定
mPara.DicDir = @"C:\*******\dic\ipadic";
//そしたら、ユーザ辞書ファイルを下の感じで指定
List<string> wkList = new List<string>();
//複数指定可
wkList.Add(@"C:\*******dic\userdic\chacha.dic");
//MeCabParamにセット
mPara.UserDic = wkList.ToArray();

↑はプログラム内でやってるけど、app.configとかで外だしもできるっぽ。

で、ほぼやりたい感じには上記でなるんだけど、コストの指定とかもう少し理解したいところ。まだ困ってないのは、大した解析やってないからでしょうし。。。

C# パフォーマンスカウンタもどき

システムの保守とかしてると、サーバのCPUとかメモリとかシステムのプロセスとかディスクの容量とか統計を取りたくなる。WindowsOSの話。パフォーマンスカウンタでいけるみたいな話はありますが、なんか使い勝手が悪いのと裏でずっと動かしてていいのかなんか微妙。あと結果のファイルもなんか微妙。

とゆーわけで自分でサービスを作った話。

パフォーマンスカウンタ使うには、最初にSystem.Managementを参照に追加しないとだめ。そしたら↓の感じで見たいカウンタ(項目)別に設定する。

//using System.Management;

//CPUの使用率
PerformanceCounter pc = new PerformanceCounter("Processor", "% Processor Time", "_Total", true);
Console.WriteLine(pc.NextValue());

で、どんな指定が出来るのかの一覧があんまない。環境によるってことかしらね。なので、何が使えるのか最初に↓の感じで一覧作ってCSVあたりにはいておく。

//PerformanceCounterCategoryを使う場合は↓のusingが必要
//using System.Diagnostics;
public static void ListUpCounters()
{
    using (StreamWriter wr = new StreamWriter(@".\パフォーマンスカウンタ一覧.csv", false, Encoding.GetEncoding(932)))
    {
        //カテゴリの一覧
        PerformanceCounterCategory[] pccates = PerformanceCounterCategory.GetCategories();
        wr.WriteLine(String.Format("カテゴリ,タイプ,オブジェ,単位,説明"));//一覧用ヘッダ
        foreach (PerformanceCounterCategory tmpcate in pccates)
        {
            //カテゴリのオブジェクト名
            wr.WriteLine(String.Format("\"{0}\",,,,\"{1}\"", tmpcate.CategoryName, tmpcate.CategoryHelp));
            try
            {
                //カテゴリのインスタンス一覧を取得
                string[] instnames = tmpcate.GetInstanceNames();
                //インスタンス名を出力
                foreach (string instanceName in instnames)
                {
                    wr.WriteLine(String.Format("{0},INSTANCE,\"{1}\"", tmpcate.CategoryName, instanceName));
                }

                //カウンタの一覧
                PerformanceCounter[] counters;
                if (instnames.Length > 0) counters = tmpcate.GetCounters(instnames[0]);
                else counters = tmpcate.GetCounters();

                //パフォーマンスカウンタ名を表示
                foreach (PerformanceCounter cnt in counters)
                {
                    wr.WriteLine(String.Format("{0},COUNTER,{1},\"{2}\",\"{3}\"", tmpcate.CategoryName, cnt.CounterName, cnt.CounterType, cnt.CounterHelp));
                }
            }
            //インスタンス情報なしはInvalidOperationExceptionらしい
            catch (InvalidOperationException)
            {
                wr.WriteLine(String.Format("インスタンス情報なし"));
            }
        }
    }

}

動かしたときのプロセスとかスレッドとかも全部出てくるので結構多いかも。。。

基本的にはCPUとメモリとディスクあたりを見る感じかしら。

カテゴリ カウンタ インスタンス 中身
Processor % Processor Time _Total CPU使用率(%)
Memory Available Bytes 利用可能メモリ(B)
Memory Pages/sec ページ数
LogicalDisk % Free Space _Total ディスク全体空率(%)
LogicalDisk Free Megabytes _Total ディスク全体空容量(MB)
LogicalDisk Current Disk Queue Length _Total ディスク待ちキュー数

インスタンスないやつは指定するときは引数無しで。

あと、カテゴリで「Process」の奴は個別のプロセスのCPU使用率とかも見れる。 インスタンスはプロセスの名前。同じ名前で何個もできる場合は「プロセス名#2」みたいな番号のつけ方される。固定で名称指定は無理だと思うので、↓の感じで、そのカテゴリのインスタンスを全部取って、プロセス名が含まれてたら使うみたいなやり方。

public static void proccessval()
{
    var catelist = PerformanceCounterCategory.GetCategories().ToList();
    foreach (PerformanceCounterCategory tmp in catelist.Where(n=>n.CategoryName=="Process"))
    {
        try
        {
            string[] instnames = tmp.GetInstanceNames();
            foreach (string instanceName in instnames)
            {
                if (instanceName.Contains("w3wp"))//例えばIIS
                {
                    PerformanceCounter pc = new PerformanceCounter("Process", "% Processor Time", instanceName, true);//CPU使用率(%)
                    Console.WriteLine(pc.NextValue());
                    pc = new PerformanceCounter("Process", "Working Set", instanceName, true);//使用メモリ(B)
                    Console.WriteLine(pc.NextValue());
                }
            }
        }
        catch
        { }
    }
}

ディスクについてはドライブ単位に見たい場合も↑と同じノリで出来る。あと、プロセスの場合、そのプロセスのコマンドラインが欲しかったりする場合がある。その場合は↑の奴に↓の感じで追加。

public static void proccessval()
{
    var catelist = PerformanceCounterCategory.GetCategories().ToList();
    foreach (PerformanceCounterCategory tmp in catelist.Where(n=>n.CategoryName=="Process"))
    {
        try
        {
            string[] instnames = tmp.GetInstanceNames();
            foreach (string instanceName in instnames)
            {
                if (instanceName.Contains("w3wp"))
                {
                    PerformanceCounter pc = new PerformanceCounter("Process", "% Processor Time", instanceName, true);//CPU使用率(%)
                    Console.WriteLine(pc.NextValue());
                    pc = new PerformanceCounter("Process", "Working Set", instanceName, true);//使用メモリ(B)
                    Console.WriteLine(pc.NextValue());

                    //プロセスIDをとってコマンドライン取得する
                    PerformanceCounter tmppc = new PerformanceCounter("Process", "ID Process", instanceName, true);
                    tmppc.NextValue();
                    Process ps = Process.GetProcessById(Convert.ToInt32(tmppc.NextValue()));
                    string proccessid = Convert.ToInt32(tmppc.NextValue()).ToString();

                    ManagementClass mc = new ManagementClass(new ManagementPath("Win32_Process"));
                    foreach (ManagementObject obj in mc.GetInstances())
                    {
                        if (obj["ProcessId"] != null && obj["CommandLine"] != null)
                        {
                            if (obj["ProcessId"].ToString() == proccessid)
                            {
                                string commandline = obj["CommandLine"].ToString();
                                string proccaption = obj["Caption"].ToString();
                            }
                        }
                    }

                }
            }
        }
        catch
        { }
    }
}

で、あとはこれをWindowsサービスで作って、とりたいカウンタの値を10秒単位とか1分単位とかでログファイルに書くみたいな感じ。

あーあと、毎回新しくカウンタ作るでもいいかもだけど重そう。。。最初に初期化だけして使いまわす場合、最初は存在したプロセスが途中でいなくなったりすると例外で落ちるので、その場合は初期化からまたやり直しみたいなロジック入れないとダメかな。あと途中でプロセス増えた場合もダメなので、定期的に初期化し直すように組まないとだめかしらね。

SQLServer サロゲートペアがあるかどうか調べるクエリ


--//上位サロゲート:U+D800 ~ U+DBFF
--//下位サロゲート:U+DC00 ~ U+DFFF
--//C#での正規表現:"[\uD800-\uDBFF][\uDC00-\uDFFF]"

select * from TEST where 対象文字 collate japanese_bin like N'%[' + nchar(0xD800) +N'-' + nchar(0xDBFF) + N'][' + nchar(0xDC00)+'-'+ nchar(0xDFFF) + N']%'

Unicode的表現だと↓でもいけるはずなんだけど上手くいかなかった。

select * from TEST where 対象文字 like '%[' + nchar(0x10000) + '-' + nchar(0xFFFFF) + ']%' 

サロゲ以外の漢字も抜けてくる。。。

漢字だけだと0x20000~の範囲なので、↓でもいいと思うけど大丈夫かはわからない。

select * from TEST where 対象文字 like '%[' + nchar(0x20000) + '-' + nchar(0xFFFFF) + ']%'

うえの範囲だと普通の漢字は出てこなくなる。なんででしょね。

問合せ