So-net無料ブログ作成

Jsonファイルの読込み、書込み [VB.NET]

データを保存したり、マスターデータを読み込んだりしたいときは
CSVでデータファイルを作っていたのを、Jsonファイルにしてみた。

Jsonファイルの読み書きは、DataContractJsonSerializerを使用すれば簡単に使える。
Jsonファイルの内容をクラスとして扱える(シリアル化、逆シリアル化)ようになる。
使用するには、参照で System.Runtime.Serialization を追加する。

Dim trend As New TrendGroup

'TrendGroup.jsonを読み込んで、TrendGroupクラスにデータを格納する。
Util.ReadJson(Of TrendGroup)("C:\TrendGroup.json", trend)

'何か処理

'TrendGroupクラスのデータをTrendGroup.jsonに保存する。
Util.WriteJson(Of TrendGroup)("C:\TrendGroup.json", trend)

ファイル名:Util.vb
Imports System.Runtime.Serialization.Json

Public Class Util

    ''' <summary>
    ''' JSONファイルの書込
    ''' </summary>
    ''' <typeparam name="T"></typeparam>
    ''' <param name="path"></param>
    ''' <param name="target"></param>
    ''' <returns></returns>
    Public Shared Function WriteJson(Of T)(path As String, ByRef target As T) As Boolean

        Dim encUTF8 As System.Text.Encoding = New System.Text.UTF8Encoding(False)
        Dim resValue As Boolean = True
        Dim memory As New IO.MemoryStream
        Try
            'クラスのデータをJSON形式に変換(シリアル化)
            Dim serializer As New DataContractJsonSerializer(GetType(T))
            serializer.WriteObject(memory, target)
            Dim json As String = encUTF8.GetString(memory.ToArray())
            'データの書込
            Dim writer As New IO.StreamWriter(path, False, encUTF8)
            Try
                writer.Write(json)
            Catch ex As Exception
                resValue = False
            Finally
                writer.Close()
            End Try
        Catch ex As Exception
            resValue = False
        Finally
            memory.Close()
        End Try

        Return resValue

    End Function

    ''' <summary>
    ''' JSONファイルの読込
    ''' </summary>
    ''' <typeparam name="T"></typeparam>
    ''' <param name="path"></param>
    ''' <param name="target"></param>
    ''' <returns></returns>
    Public Shared Function ReadJson(Of T)(ByVal path As String, ByRef target As T) As Boolean

        Dim encUTF8 As System.Text.Encoding = New System.Text.UTF8Encoding(False)
        Dim resValue As Boolean = True
        Dim reader As New IO.StreamReader(path, encUTF8)
        Try
            'データの読込
            Dim json As String = reader.ReadToEnd()
            Dim memory As New IO.MemoryStream(encUTF8.GetBytes(json))
            Try
                'JSON形式のデータをクラスに変換(逆シリアル化)
                Dim serializer As New DataContractJsonSerializer(GetType(T))
                target = DirectCast(serializer.ReadObject(memory), T)
            Catch ex As Exception
                resValue = False
            Finally
                memory.Close()
            End Try
        Catch ex As Exception
            resValue = False
        Finally
            reader.Close()
        End Try

        Return resValue

    End Function

End Class


Order:=0とか付けておくと、保存したときにOrderの順番で保存される。
付けないとアルファベット順に保存される。(順番は読み書きするには問題ないけど)
ファイル名:TrendGroup.vb
Imports System.Runtime.Serialization.Json

<DataContract()>
Public Class TrendGroup

    <DataContract()>
    Public Class Axis
        <DataMember(Order:=0)> Public Property Minimum As Double
        <DataMember(Order:=1)> Public Property Maximum As Double
        <DataMember(Order:=2)> Public Property Interval As Double
    End Class

    <DataContract()>
    Public Class Trend
        <DataMember(Order:=0)> Public Property TrendNo As Integer
        <DataMember(Order:=1)> Public Property AxisType As Integer
        <DataMember(Order:=2)> Public Property LineColor As Integer
    End Class

    <DataContract()>
    Public Class Member
        <DataMember(Order:=0)> Public Property GroupNo As Integer
        <DataMember(Order:=1)> Public Property GroupName As String
        <DataMember(Order:=2)> Public Property Axis1 As Axis
        <DataMember(Order:=3)> Public Property Axis2 As Axis
        <DataMember(Order:=4)> Public Property Trend As Trend()
    End Class

    <DataMember(Order:=0)> Public Property TrendGroup As Member()

End Class


Jsonファイルを書き込むと、整形しないと見づらくなるので整形用ツールを用意しておくと便利。
Jsonファイルは、UTF-8(BOMなし)で保存する。
ファイル名:TrendGroup.json
{
    "TrendGroup": [
        {
            "GroupNo": 1,
            "GroupName": "グループNo1",
            "Axis1": {
                "Minimum": -30,
                "Maximum": 30,
                "Interval": 5
            },
            "Axis2": {
                "Minimum": 0,
                "Maximum": 100,
                "Interval": 5
            },
            "Trend": [
                {
                    "TrendNo": 1,
                    "AxisType": 0,
                    "LineColor": -65536
                },
                {
                    "TrendNo": 2,
                    "AxisType": 0,
                    "LineColor": -23296
                },
                {
                    "TrendNo": 3,
                    "AxisType": 1,
                    "LineColor": -256
                }
            ]
        },
        {
            "GroupNo": 2,
            "GroupName": "グループNo2",
            "Axis1": {
                "Minimum": 0,
                "Maximum": 60,
                "Interval": 5
            },
            "Axis2": {
                "Minimum": 0,
                "Maximum": 100,
                "Interval": 5
            },
            "Trend": [
                {
                    "TrendNo": 4,
                    "AxisType": 0,
                    "LineColor": -65536
                },
                {
                    "TrendNo": 5,
                    "AxisType": 0,
                    "LineColor": -23296
                },
                {
                    "TrendNo": 6,
                    "AxisType": 1,
                    "LineColor": -256
                }
            ]
        }
    ]
}

WinMergeのJSONプラグインを作成してみた [その他]

JSONファイルが何かと便利!と色々使ってみてるけど、
元データとの比較が面倒だったりしたのでWinMergeのJSONプラグインを作成したときのメモ。

JSONファイルを整形して比較する。
整形には、JavaScriptのJSONを使用する。
WinMergeのプラグインは、VBScriptで作成されている。
WinMergeのプラグインで、JavaScriptは動くのか?(動いた!)
でも、JSONを使用すると「'JSON' は宣言されていません」と出る。
json2.jsを使用すれば出来た!

json2.js
https://github.com/douglascrockford/JSON-js

作成したプラグインと、json2.jsをMergePluginsフォルダに入れます。

ファイル名:CompareJsonFiles.sct
<scriptlet>

<implements type="Automation" id="dispatcher">
	<property name="PluginEvent">
		<get/>
	</property>
	<property name="PluginDescription">
		<get/>
	</property>
	<property name="PluginFileFilters">
		<get/>
	</property>
	<property name="PluginIsAutomatic">
		<get/>
	</property>
	<method name="UnpackFile"/>
	<method name="PackFile"/>
</implements>

<script language="JavaScript" src="json2.js"></script>
<script language="JavaScript">
function getJson(filename) {
	return jsonParse(adoLoadText(filename, "utf-8"));
}
function jsonParse(text) {
	try {
		var obj = JSON.parse(text); 
		return JSON.stringify(obj, null, "    ");
	} catch(e) {
		return e.message;
	}
}
function adoLoadText(filename, charset) {
	var stream, text;
	stream = new ActiveXObject("ADODB.Stream");
	stream.type = 2;
	stream.charset = charset;
	stream.open();
	stream.loadFromFile(filename);
	text = stream.readText(-1);
	stream.close();
	return text;
}
</script>

<script language="VBS">

Option Explicit

Function get_PluginEvent()
	get_PluginEvent = "FILE_PACK_UNPACK"
End Function

Function get_PluginDescription()
	get_PluginDescription = "Json形式のファイルを整形し比較をする。"
End Function

Function get_PluginFileFilters()
	get_PluginFileFilters = "\.json$"
End Function

Function get_PluginIsAutomatic()
	get_PluginIsAutomatic = True
End Function

Function UnpackFile(fileSrc, fileDst, pbChanged, pSubcode)
	Dim fso
	Dim foDst
	
	Set fso = CreateObject("Scripting.FileSystemObject")
	
	Set foDst = fso.OpenTextFile(fileDst, 2, True)
	foDst.Write(getJson(fileSrc))
	foDst.Close
	
	Set foDst = Nothing
	Set fso = Nothing
	
	pbChanged = True
	pSubcode = 0
	UnpackFile = True
End Function

Function PackFile(fileSrc, fileDst, pbChanged, pSubcode)
	PackFile = False
End Function

</script>
</scriptlet>


WinMergeのプラグインは、JavaScriptでも良いんじゃないのかな?

DataGridViewの選択行を元の行に戻す [VB.NET]

DataGridViewでDataSourceを設定すると選択行が先頭行になる。
選択行を覚えておき、元の位置に戻すプログラムのメモ。
'現在選択している行を取得
Dim rowFirst As Integer = 0
Dim rowCurrent As Integer = 0
If DataGridView1.CurrentCell IsNot Nothing Then
    rowFirst = DataGridView1.FirstDisplayedScrollingRowIndex
    rowCurrent = DataGridView1.CurrentCell.RowIndex
End If

DataGridView1.DataSource = dt

'選択していた行へ復旧
If DataGridView1.CurrentCell IsNot Nothing Then
    DataGridView1.FirstDisplayedScrollingRowIndex = rowFirst
    DataGridView1.CurrentCell = DataGridView1(0, rowCurrent)
End If

DataGridViewのセル結合 [VB.NET]

DataGridViewでセルを結合するためには、自分でセルを結合したかのように描画する必要があるみたい。

そこで次の表を作成したいときのプログラムメモ。
表.png

・表を見せるのみです。
・行列の追加、編集、削除や、セル幅の変更は考慮していません。
・行列のヘッダーは無効にしています。
・DataGridViewコントロールの名前は、「DataGridView1」です。
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

    Init()

End Sub

Private Sub Init()

    'ちらつき防止
    Dim type As System.Type = GetType(DataGridView)
    Dim propertyInfo As System.Reflection.PropertyInfo = type.GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.NonPublic)
    propertyInfo.SetValue(DataGridView1, True, Nothing)

    DataGridView1.AllowUserToAddRows = False
    DataGridView1.ReadOnly = True
    DataGridView1.AllowUserToDeleteRows = False
    DataGridView1.ColumnHeadersVisible = False
    DataGridView1.RowHeadersVisible = False

    Dim i As Integer
    Dim j As Integer
    Dim colAdd As DataGridViewColumn
    For i = 1 To 3
        colAdd = New DataGridViewColumn
        colAdd.Name = String.Format("Column{0}", i)
        colAdd.Width = 60
        colAdd.CellTemplate = New DataGridViewTextBoxCell
        DataGridView1.Columns.Add(colAdd)
    Next

    '結合したいセルには同じテキストを入れておく
    Dim cellText(,) As String = {
        {"店舗", "店舗", "売上"},
        {"店舗名", "担当エリア", "売上"},
        {"北支店", "駅北", "10000"},
        {"北支店", "駅南", "9000"},
        {"西支店", "商店街", "12000"},
        {"東支店", "住宅区", "8000"},
        {"南支店", "商業区", "11000"}
    }
    Dim rowAdd As DataGridViewRow
    For i = 0 To 6
        rowAdd = New DataGridViewRow
        rowAdd.CreateCells(DataGridView1)
        For j = 0 To rowAdd.Cells.Count - 1
            rowAdd.Cells(j).Value = cellText(i, j)
        Next
        DataGridView1.Rows.Add(rowAdd)
    Next

End Sub

Private Sub DataGridView1_CellPainting(sender As Object, e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting

    '結合したいセルをここに書く
    MergeCell(e, New Point(0, 0), New Point(1, 0))
    MergeCell(e, New Point(2, 0), New Point(2, 1))
    MergeCell(e, New Point(0, 1), New Point(0, 1))
    MergeCell(e, New Point(1, 1), New Point(1, 1))

End Sub

'Cell1には、セルの開始位置(X, Y)
'Cell2には、セルの終了位置(X, Y)
Private Sub MergeCell(ByRef e As System.Windows.Forms.DataGridViewCellPaintingEventArgs, Cell1 As Point, Cell2 As Point)

    If (e.RowIndex >= Cell1.Y AndAlso e.RowIndex <= Cell2.Y) AndAlso (e.ColumnIndex >= Cell1.X AndAlso e.ColumnIndex <= Cell2.X) Then

        Dim rect As New Rectangle With {.X = 0, .Y = 0, .Width = 0, .Height = 0}
        Dim i As Integer

        '開始セルの位置
        '結合セルが画面外にあるときの位置を考慮
        For i = Cell1.Y + 1 To DataGridView1.FirstDisplayedScrollingRowIndex
            rect.Y -= DataGridView1(Cell1.X, i - 1).Size.Height
        Next
        For i = DataGridView1.FirstDisplayedScrollingRowIndex + 1 To Cell1.Y
            rect.Y += DataGridView1(Cell1.X, i - 1).Size.Height
        Next
        '結合セルが画面外にあるときの位置を考慮
        For i = Cell1.X + 1 To DataGridView1.FirstDisplayedScrollingColumnIndex
            rect.X -= DataGridView1(i - 1, Cell1.Y).Size.Width
        Next
        For i = DataGridView1.FirstDisplayedScrollingColumnIndex + 1 To Cell1.X
            rect.X += DataGridView1(i - 1, Cell1.Y).Size.Width
        Next

        '終了セルの幅
        For i = Cell1.Y To Cell2.Y
            rect.Height += DataGridView1(Cell2.X, i).Size.Height
        Next
        For i = Cell1.X To Cell2.X
            rect.Width += DataGridView1(i, Cell2.Y).Size.Width
        Next

        'セル位置の補正
        rect.X += 1
        rect.Y += 1

        'グラデーションをかけてヘッダーぽく見せる
        Dim gb As New System.Drawing.Drawing2D.LinearGradientBrush(rect, SystemColors.ControlLightLight, SystemColors.Control, System.Drawing.Drawing2D.LinearGradientMode.Vertical)
        gb.GammaCorrection = True

        '通常の塗りつぶし
        'e.Graphics.FillRectangle(New SolidBrush(SystemColors.Control), rect)
        e.Graphics.FillRectangle(gb, rect)
        e.Graphics.DrawRectangle(New Pen(DataGridView1.GridColor), rect)

        gb.Dispose()

        '描画するセル位置の文字をヘッダーテキストとして表示
        Dim headerText As String = DataGridView1(e.ColumnIndex, e.RowIndex).Value
        TextRenderer.DrawText(e.Graphics, headerText, e.CellStyle.Font, rect, e.CellStyle.ForeColor, TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter)

        e.Handled = True

    End If

End Sub

2016/01/27 結合セルが半分だけ画面外の場合を考慮
実行した表.png

FillRectangleで塗りつぶしが出来ない [VB.NET]

FillRectangleとDrawRectangleで塗りつぶされる領域が違ってた。

DrawRectangleより、Width, Height が、それぞれ1ずつ少なく塗りつぶされていた。

Dim g As Graphics = e.Graphics
Dim rect As New Rectangle(20, 20, 120, 120)

g.DrawRectangle(Pens.Blue, rect)
g.FillRectangle(Brushes.Red, rect)

g.Dispose()


FillRectangle.png

SQLite3へ大量のCSVを一括で取り込む [その他]

SQLite3へCSVファイルを取り込むためのスクリプトのメモ

次の必要なファイルを同じフォルダに配置する。
・sqlite3.exe
・CSVを取り込む先のテーブル(CSV_DATA)が作成されたDB(Sample.sqlite3)
・csv_import.bat
・INPUTフォルダを作成し、取り込む対象のCSVファイルを入れる。

*sqlite3.exeは http://www.sqlite.org/ から sqlite-shell-win32-x86-3090200.zip をダウンロードする。
*CSV_DATAテーブルの内容は、DATETIME型、後はREAL型が100個。

csv_import.batを実行すると、OUTPUTフォルダにINPUTフォルダ内のCSVファイルを結合した
merge.csvが作成され、それをDBに取り込む。

・csv_import.bat
@ECHO OFF

ECHO DB内のCSVデータを削除
sqlite3 Sample.sqlite3 "delete from CSV_DATA"

ECHO CSVを結合
mkdir .\OUTPUT
type .\INPUT\*.csv > .\OUTPUT\merge.csv

ECHO CSVをDBに取込
sqlite3 -separator , Sample.sqlite3 ".import ./OUTPUT/merge.csv CSV_DATA"

ECHO.
ECHO 処理が完了しました。
ECHO 終了するには何かキーを押してください...
PAUSE > NUL


・取り込むCSVファイルの内容
数値データは、100個あります。
2015/12/01 00:00:00,     0,     0.1,     0,     0,     0,     0,     0,     0,   830,     0,     0,     0,     0,  1000
2015/12/01 00:01:00,     0,     0.2,     0,     0,     0,     0,     0,     0,   830,     0,     0,     0,     0,  1000
2015/12/01 00:02:00,     0,     0.1,     0,     0,     0,     0,     0,     0,   830,     0,     0,     0,     0,  1000
2015/12/01 00:03:00,     0,     0.3,     0,     0,     0,     0,     0,     0,   653,     0,     0,     0,     0,     0
2015/12/01 00:04:00,     0,     0.1,     0,     0,     0,     0,     0,     0,   657,     0,     0,     0,     0,     0
2015/12/01 00:05:00,     0,     0.2,     0,     0,     0,     0,     0,     0,   661,     0,     0,     0,     0,     0
2015/12/01 00:06:00,     0,     0.3,     0,     0,     0,     0,     0,     0,   653,     0,     0,     0,     0,     0
2015/12/01 00:07:00,     0,     0.3,     0,     0,     0,     0,     0,     0,   653,     0,     0,     0,     0,     0
2015/12/01 00:08:00,     0,     0.4,     0,     0,     0,     0,     0,     0,   827,     0,     0,     0,     0,  1000
2015/12/01 00:09:00,     0,     0.3,     0,     0,     0,     0,     0,     0,   830,     0,     0,     0,     0,  1000
2015/12/01 00:10:00,     0,     0.4,     0,     0,     0,     0,     0,     0,   830,     0,     0,     0,     0,  1000


Kindle for Android のフォントを変更する [Android]

Kindle for Android バージョン 4.18.0.106

いつもはコミックばっかりだけど、久しぶりに小説を購入して見たら
フォントが縦書きに対応してなくて、フォントを変更しようにも「Droid Serif」しか選べなかった。
小説とかは「明朝で見たい!」のでフォントを変更してみた。

・PCに接続とかせずに、スマホのみで変更した。
・フォントは、IPAex明朝を使用した。
・root権限は必要なし。
・Kindleの設定ファイルの書き換えが必要。
zipファイルの解凍や、フォントのコピー、設定ファイルの書き換えには「ファイル マネージャー」を使用しました。

Kindle for Android はタスクからも終了しておくこと。
1.IPAex明朝をダウンロードし、zipファイルを解凍して「ipaexm.ttf」を取り出す。
2.storage/emulated/0/Android/data/com.amazon.kindle/files/fonts/ja/ に「ipaexm.ttf」をコピーする。
3.同フォルダ内のsystem_fons.xmlをテキストエディタで開き一番最初に見つかった
/storage/emulated/0/Android/data/com.amazon.kindle/files/fonts/ja/TBGothicMed_213.ttf
を次のように書き換える。(フォントのファイル名をIPAex明朝にする)
/storage/emulated/0/Android/data/com.amazon.kindle/files/fonts/ja/ipaexm.ttf
4.上書き保存をして、Kindleを起動すればフォントが変わっている!はず。

参考URL
IPAexフォント ダウンロードページ
http://ipafont.ipa.go.jp/old/ipaexfont/download.html
Kindle for AndroidのフォントをIPA明朝体に変更する@Xperia Z Ultra
http://www.borg-nikki.com/?p=2518
Android版 Kindleアプリの日本語フォントを変えてみたよ
http://www.tecking.org/archives/3077

プロセス毎のCPU使用率、メモリ使用量のログを取得する [その他]

プロセス毎にCPU使用率や、メモリ使用量のログを取得したい。
パフォーマンスモニターでログを取得しても良かったけど
1日に1~2回程度で良いので長期間ロギングをする。

WMICというコマンドを使用する。
batファイルに次のコマンドを書いて、タスクスケジューラで実行させた。
@ECHO OFF
SET logDate=%date:~0,4%%date:~5,2%%date:~8,2%
WMIC /OUTPUT:C:\PROLOG\PROLOG_%logDate%.log PROCESS GET /FORMAT:LIST

出力されたログファイルの内容は、色々な項目がある。
WorkingSetSize:物理メモリ使用量
PageFileUsage:ページングファイルの使用量

AssemblyAttributes.vbが見つからないエラーの対処 [VB.NET]

Windows 7 Professional 32ビット、VB2010を使用している環境で
急に次のエラーが表示されるようになった。
エラー 1 モジュール ファイル 'C:\Users\Administrator\AppData\Local\Temp\.NETFramework,Version=v4.0.AssemblyAttributes.vb' を開けません: 指定されたファイルが見つかりません。
対処方法は次のどれかで対処する。
1.プロジェクトの拡張子がsuoのファイルを削除すると出なくなる。
2.リビルドすると出なくなる。
3.ソリューション構成を「Debug」や「Release」に切り替えると出なくなる。

なんとなく3がおすすめ。
1.ファイルを消すのが何かいや。
2.色々更新されるからいや。とくにexeが。
3.何もしないから好き。

ExcelのVLOOKUPで見つからない場合は空白にしたい [その他]

資料を作ったりするときにExcelをよく使います。
このとき、VLOOKUP関数がすごく役にたつのだけど
一覧から見つからないときは、0を返したりするので、
IFで判断して空白に変えたりとがんばってました。

でも、これだけで、VLOOKUPで見つからない場合0ではなく、空白を返すことが出来ます。
参考URL
http://officetanaka.net/excel/function/tips/tips48.htm

VLOOKUP関数の最後に &"" を付け加えるだけ。

具体例
=VLOOKUP(A1,$F$1:$K$100,3,FALSE)&""