VB.NETでも形態素解析っぽいことをしてExcel方眼紙から漢字を抽出したい
はじめに
皆さんは漢字は得意ですか? 弊社はクッソ苦手です。
パソコンに頼り切っちゃってるのでまぁ当たり前っちゃー当たり前だし、普段パソコンで文章を書くなら別に困りもしないのですが、打ち合わせの時に書記でホワイトボードに書くときに漢字が書けなくて困るんですよね。
そこで思ったのですが、似たような打ち合わせなら似たような漢字が出てくるのでは? つまり議事録から漢字を引っこ抜けば似たような打ち合わせに適用できるのでは?
そして議事録といえばExcel。表計算からDB設計からポスター作製までをこなすオールラウンドアプリケーション。
つまり
すればその打ち合わせで使われる漢字の傾向が掴めるって寸法です。
この時点で自分でも思っています。
素直に漢字を勉強しろよ
Excelから文字列の取り出し
とりあえずExcelからデータを抜かないといけません。
ここではEPPlusを使ってデータを取り出します。
ライブラリのインストールはNuGetからサクッとインストールしてください。
今回は特にエクセルの見た目なんてものには興味がなく、ただ文字を引っこ抜ければいいので
- ディレクトリ直下のエクセルファイルをひたすら列挙し
- すべてのシートのすべてのセルから文字列を取り出す
という感じでやってます。
Imports OfficeOpenXml Imports System.IO Public Class ExcelContentExtractor Private Sub New() ' Nothing to do. End Sub Public Shared Function ExtractContent(targetDirectory As String) As IEnumerable(Of String) Dim contents = New List(Of String) For Each it In Directory.GetFiles(targetDirectory, "*.xlsx") Using exc = New ExcelPackage(New FileInfo(it)) For Each sheet In exc.Workbook.Worksheets For Each cell In sheet.Cells() Dim text = TryCast(cell.Value, String) If Not text Is Nothing Then contents.Add(text) End If Next Next End Using Next Return contents End Function End Class
形態素解析
形態素解析はMeCabの.NETの移植版であるNMeCabを使用しています。
インストールとか使い方は完全にフィーリングでやったので解説出来ないです。ごめんね
Imports NMeCab Public Class SentenceSplitter Private Sub New() ' Nothing to do. End Sub Public Shared Function Split(sentence As IEnumerable(Of String)) As IEnumerable(Of String) Dim param = New MeCabParam() With {.DicDir = ".\ipadic"} Dim words = New List(Of String) Using mecab = MeCabTagger.Create(param) For Each it In sentence Dim node = mecab.ParseToNode(it) While Not node Is Nothing If node.Feature Is Nothing Then node = node.Next Continue While End If words.Add(node.Surface) node = node.Next End While Next End Using return words End Function End Class
このあたりを試行錯誤してる時も常に頭の片隅にありました。
素直に漢字を勉強しろよ
漢字のフィルタリング
次に漢字を含む単語をフィルタリングします。
ヤフーでググるとAscW
でUnicodeスカラ値を取り出しIf
で判断するというとっても原始的素朴でステキな方法が紹介されていました。*1
.NETの正規表現は名前付きブロックをサポートしているのでありがたくそれを使いましょう。
特に下調べしてないんで確証は無いんですがまぁCJK統合漢字を含んでれば大丈夫でしょうという感じで行きました。 好みで拡張Aもあわせてどうぞ。
Imports System.Text.RegularExpressions Public Class HanFilter Private Sub New() ' Nothing to do. End Sub Public Shared Function HasHan(word As String) As Boolean Return Regex.IsMatch(word, ".*[\p{IsCJKUnifiedIdeographs}]+.*") End Function End Class
集計
あとはLINQで一発なのでもういいや。
Module Program Sub Main(args as String()) If args.Length <> 1 Then Console.WriteLine("AppName <path of directory that contains the Excel file>") Environment.Exit(0) End If Dim contents = ExcelContentExtractor.ExtractContent(args(0)) Dim words = SentenceSplitter.Split(contents) Dim result = words. Where(AddressOf HanFilter.HasHan). GroupBy(Function(it) it, Function(it) 1). Select(Function(it) New With {.Count = it.Sum(), .Word = it.Key}). OrderByDescending(Function(it) it.Count) for each it in result Console.WriteLine("「{0}」: {1}", it.Word, it.Count) Next End Sub End Module
「漢字」: 12 「文字」: 5 「素」: 4 「形態」: 4 「解析」: 4 「打ち合わせ」: 4 「似」: 3 「議事」: 3 「録」: 3 「列」: 3 「取り出し」: 3 「弊社」: 2 「当たり前」: 2 「書く」: 2 「時」: 2 「思っ」: 2 「引っこ抜け」: 2 「抜き出し」: 2 「集計」: 2 「素直」: 2 「勉強」: 2 「特に」: 2 「感じ」: 2 「正規」: 2 「表現」: 2 「方眼」: 1 「紙」: 1 「抽出」: 1 「好き」: 1 「変数」: 1 「個」: 1 「越え」: 1 「普通」: 1 「皆さん」: 1 「得意」: 1 「苦手」: 1 「頼り」: 1 「切っ」: 1 「普段」: 1 「文章」: 1 「別に」: 1 「困り」: 1 「書記」: 1 「書け」: 1 「困る」: 1 「出」: 1 「適用」: 1 「表」: 1 「計算」: 1 「設計」: 1 「作製」: 1 「行い」: 1 「各」: 1 「要素」: 1 「使わ」: 1 「傾向」: 1 「掴める」: 1 「寸法」: 1 「時点」: 1 「自分」: 1 「抜か」: 1 「使っ」: 1 「今回」: 1 「見た目」: 1 「興味」: 1 「直下」: 1 「列挙」: 1 「取り出す」: 1 「移植」: 1 「版」: 1 「使用」: 1 「使い方」: 1 「完全」: 1 「解説」: 1 「出来」: 1 「試行」: 1 「錯誤」: 1 「常に」: 1 「頭」: 1 「片隅」: 1 「次に」: 1 「含む」: 1 「単語」: 1 「値」: 1 「判断」: 1 「原始」: 1 「的」: 1 「素朴」: 1 「方法」: 1 「紹介」: 1 「名前」: 1 「付き」: 1 「使い」: 1 「下調べ」: 1 「確証」: 1 「無い」: 1 「統合」: 1 「含ん」: 1 「大丈夫」: 1 「行き」: 1 「好み」: 1 「拡張」: 1 「一」: 1 「発」: 1
おわりに
素直に漢字を勉強しろよ
*1:しょうもないバグを作りこみそうだし、テストも面倒そう
C#でもILGeneratorでダイナミックにToStringしたい
はじめに
動的にToString
するのに定評がある[要出典]弊社ですが、ライブラリを追加するレベルでもないけど手軽に1ソースコードを追加するだけでパパッと使えるものが欲しいな~と考えておりました。
と、いうわけで作りました。
縛り内容
ゲーム実況等では縛りとしていくつかルールを決めてプレイすることがありますよね。
今回は弊社もいくつか縛りを設けて実装してみようと思います。
- 1ソースコード・100行以内で実装
- Visual Studio 2010・.NET Framework 4.0でビルドできるコードにする
2番目についてはまぁ涙を拭けよといった感じですが、まぁアレがアレですよね。
コード
おわりに
まぁやっていることはSigilを使ったアレと全く一緒なんで特に解説もいらないかな。
おわり
VB.NETでもWinDbgしたい
はじめに
この記事はWinDbgで.NET Frameworkのデバッグをするときのコマンドの備忘録です。
解説はありません。希望もありません。
ダンプの取得
ダンプは別途ソフトウェアをインストールする必要なく、タスクマネージャから取得することが可能。
しかし、ダンプを取得するプロセスのビット数に応じてタスクマネージャのビット数を選択する必要がある。
- 32bit Windows
- 常に32bitタスクマネージャ(通常起動したタスクマネージャ)
- 64bit Windows
- 64bitプロセス
- 64bitタスクマネージャ(通常起動したタスクマネージャ)
- 32bitプロセス
- 32bitタスクマネージャ
%windir%\SysWOW64\taskmgr.exe
- 32bitタスクマネージャ
- 64bitプロセス
当該プロセスがどのビット数で動作しているかはタスクマネージャの詳細
タブ→プラットフォーム
(Windows 10)から確認が可能。
SOSのロード
- ~.NET 3.5
.loadby sos mscorwks
- .NET 4 ~
.loadby sos clr
メソッドにブレークポイントを貼る
!bpmd
はsos.dll
のロードが必要。
-
!bpmd <assembly> <namespace>.<class>.<method>
!bpmd ConsoleApplication2.exe ConsoleApplication2.Module1.Add
ジェネリッククラス
!bpmd <assembly> <namespace>.<class>`<type argument count>.<method>
!bpmd mscorlib.dll System.Collections.Generic.List`1.Add
ジェネリックメソッド
How to add a breakpoint in a managed generic method in windbg (sos) – I know the answer (it's 42)
JITコンパイル済みネイティブアドレスからMethodDescを検索
0:000> !clrstack OS Thread Id: 0x2ec0 (0) Child SP IP Call Site 012ff14c 0192056e ConsoleApplication2.Module1.Hoge[[System.Double, mscorlib]](Double) [C:\Users\jyuch\Documents\Visual Studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Module1.vb @ 19] 012ff164 019204c2 ConsoleApplication2.Module1.Main() [C:\Users\jyuch\Documents\Visual Studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Module1.vb @ 10] 012ff2e8 73e81376 [GCFrame: 012ff2e8] 0:000> !ip2md 0192056e MethodDesc: 01444dcc Method Name: ConsoleApplication2.Module1.Hoge[[System.Double, mscorlib]](Double) Class: 0144159c MethodTable: 01444d70 mdToken: 06000011 Module: 01443fbc IsJitted: yes CodeAddr: 01920550 Transparency: Critical Source file: C:\Users\jyuch\Documents\Visual Studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Module1.vb @ 19
MethodDescからILをダンプ
0:000> !dumpil 01444dcc ilAddr = 00e72214 IL_0000: nop IL_0001: ldarga.s VAR OR ARG 0 IL_0003: constrained. <unknown token type 1b000000> IL_0009: callvirt System.Object::ToString IL_000e: stloc.0 IL_000f: br.s IL_0011 IL_0011: ldloc.0 IL_0012: ret
おわりに
わあい
おまけ
よさげ