パークのソフトウエア開発者ブログ|ICT技術(Java・Android・iPhone・C・Ruby)なら株式会社パークにお任せください -23ページ目

パークのソフトウエア開発者ブログ|ICT技術(Java・Android・iPhone・C・Ruby)なら株式会社パークにお任せください

開発の解決方法や新しい手法の情報を、パークのエンジニアが提供します。パークのエンジニアが必要な場合は、ぜひお気軽にお問い合わせ下さい。 株式会社パーク:http://www.pa-rk.co.jp/


Active Directory ドメイン サービス (AD DS) へPowerShellでサイト&サイトリンク追加

なかなかたどり着けなかったので。

サンプル
#サイト追加
 New-ADObject -Name 'サイト名' -Type site -Description '任意' -Path  'CN=Sites,CN=Configuration,DC=Hoge,DC=com'

#サブネット
 New-ADObject -Name '192.168.100.0/24' -Type subnet -OtherAttributes @{location="任意";siteObject="サイト名",CN=Sites,CN=Configuration,DC=Hoge,DC=com"} -Path "CN=Subnets,CN=Sites,CN=Configuration,DC=Hoge,DC=com"

#サイトリンク追加
 New-ADObject -Name 'サイトリンク名' -Type SiteLink -Path "CN=IP,CN=Inter-Site Transports,CN=Sites,CN=Configuration,DC=Hoge,DC=com" -OtherAttributes @{siteList="CN=サイト名,CN=Sites,CN=Configuration,DC=Hoge,DC=com","CN=サイト名2,CN=Sites,CN=Configuration,DC=Hoge,DC=com"; replInterval=180; cost=100}

ちかです
前回に引き続き D 言語ネタです。小ネタ未満の微ネタです。

Perl や Ruby にはステートメント修飾子なる制御構文があります。
たとえば、 Ruby の次のコードは x が偶数でないときだけ標準出力に x の値を出力します。

puts x unless x.even?

もし D 言語で似たような機能を実現するとしたらどうするか考えてみました。

import std.stdio;
import std.datetime;

unittest
{
    auto i = 0;
    i++.unless(true);  assert(i == 0);
    i++.unless(false); assert(i == 1);
}
void unless(lazy void expression, bool condition)
{
    condition || expression;
}

void main()
{
    auto x = 10;
    writeln(x, " is not even.").unless(x % 2 == 0);
    writeln(x, " is not odd.").unless(x % 2 != 0);

    auto writeObsoleteSlogan = () => writeln("TIMTOWTDI");
    (write("Hello, "), writeObsoleteSlogan())
        .unless(Clock.currTime().year >= 2000);
    (write("Goodbye, "), writeObsoleteSlogan())
        .unless(Clock.currTime().year < 2000);
}

出力 (公式サイト で 2014 年 12 月 7 日に動作確認)

10 is not odd.
Goodbye, TIMTOWTDI

20 行以上のコードをブログに貼ったところで 読んでもらえないことは分かってます。

でもチラ見してみて! 気持ち悪いでしょ!
writeln(x, " is not even.").unless(x % 2 == 0) はまだしも
i++.unless(true) とかエグい。

肝心の unless 関数の本体は condition || expression です。
D 言語では && 演算子と || 演算子の右辺は特別に void 型を返す式も許可されており、それぞれ ifunless の役割を担っています。
慣れないと読みにくいので実際に使われているかどうかは知りませんが...

それから D 言語では、

  • 仮引数を lazy 修飾すると、実引数として渡された式を遅延評価します。
  • 関数呼び出しを第 1 引数のメソッド呼び出しのような形式で記述できます (UFCS (Uniform Function Call Syntax))。

という機能があるため、 《第 1 引数: 遅延評価される式》.《関数名》(《第 2 引数: 条件》) という記述ができてしまいます。

同じ要領で until 修飾子や rescue 修飾子みたいなこともできます。

unittest
{
    auto i = 0, sum = 0;
    (i++, sum += i).until(i == 5);
    assert(i == 5);
    assert(sum == 15);
    (sum += i).until(++i == 10);
    assert(i == 10);
    assert(sum == 45);
}
void until(lazy void expression, lazy bool condition)
{
    while (!condition) expression;
}
unittest
{
    void throws() { throw new Exception("thrown"); }
    void nothrows() nothrow { }    auto i = 0;
    nothrows().rescue(i++);        assert(i == 0);
    throws().rescue(i++);          assert(i == 1);
    nothrows().rescue!(ex => i++); assert(i == 1);
    throws().rescue!(ex => i++);   assert(i == 2);
}
void rescue(lazy void expression, lazy void onexception)
{
    try { expression; }
    catch (Exception) { onexception; }
}
void rescue(alias onexception)(lazy void expression)
{
    try { expression; }
    catch (Exception ex) { onexception(ex); }
}

この記事はあくまでネタです。
言語機能をちょっと試してみただけですので
良い子はマネしないでね!

p.s.
上の unlessuntil は条件式として bool 型しか受け付けていませんが、
他の型も真偽値として扱えるので (型パラメーターを取るなりして) 受け付けた方がいいですね。

こんにちは。ひらのです。

Excel VBA で一時DB⇔ADOを使用する方法の最終回です。
最後はDB(ADO)に登録したデータの削除についてご紹介します。

■前提知識

  • 簡単なExcel VBAコードを読めること

■お題(要件)

  1. 任意のフォーマットの文字列を解釈したい(SELECT, INSERT)
  2. 特定のデータを更新したい(UPDATE)
  3. 特定のデータを削除したい(DELETE) ★この記事ではコレを紹介します

■特定のデータを削除したい(DELETE)

Excel VBA で一時DBを使用する ― INSERTとSELECT では、
下記データをADOに登録しました。

[2014/10/01 7:40:58.112] RCV command=ACK ID=110405 messageId=001 data=
[2014/10/01 7:40:58.303] SND command=ACK ID=110405 messageId=002 data=
[2014/10/01 7:40:58.579] RCV command=DATA ID=110405 messageId=501 data=14B4 88B7 D54B A0D2 9A6A 3935


この記事ではADOに登録した内容にて、
特定のIDを持つレコードを削除したいと思います。

前々回作成した標準モジュールMyDBに、下記関数を追加します。

Public Sub DeleteWithSelectedId(ByVal id As Long)
    If 0 = adoRecordset.RecordCount Then
        Exit Sub
    End If
    
    Dim criteria As String
    criteria = "id = '" & id & "'"
    
    adoRecordset.MoveFirst
    Do While True
        Call adoRecordset.Find(criteria, 0, adSearchForward)
        If adoRecordset.EOF Then
            Exit Do
        End If
        adoRecordset.Delete
        adoRecordset.MoveNext
    Loop
End Sub

特定のレコードを検索する方法は Excel VBA で一時DBを使用する ― UPDATE と同じです。
今回は検索したレコードを15行目で削除しています。

ポイントは、DELETE() の前後の処理です。

まずは12行目の分岐からご説明します。
Find() して、最後のレコードまで検索済み ⇔ EOF が TRUE の状態で DELETE() を実行すると、
下記のエラーが発生します。

実行時エラー '3021':
BOF と EOF のいずれかが True になっているか、または現在のレコードが削除されています。
要求された操作には、現在のレコードが必要です。

なので、Find() で検索したら Delete() 前にEOFかどうかを確認します。


続いて16行目。
Delete() を実行すると、カレントレコードが設定されていない状態になってしまいます。

この状態で Find() すると、下記のエラーが発生します。

実行時エラー '-2147217885(80040e23)':
行ハンドルは削除された行、または削除対象としてマークされている行を参照しました。

なので、カレントレコードを一度前後に変更してから次の操作を行ってください。
今回は次のレコードから検索を続行したいので、MoveNext() を使用しました。



Excel VBAで一時DB(ADO)を利用する方法は以上です。

ADOではソートなどもできるらしいので、後は検索してみてください(^^)/~

こんにちは。ひらのです。

Excel VBA で一時DB⇔ADOを使用する方法のご紹介、第二弾です。
今回はDB(ADO)に登録したデータの更新方法についてご紹介します。

■前提知識

  • 簡単なExcel VBAコードを読めること

■お題(要件)

  1. 任意のフォーマットの文字列を解釈したい(SELECT, INSERT)
  2. 特定のデータを更新したい(UPDATE) ★この記事ではコレを紹介します
  3. 特定のデータを削除したい(DELETE)

■特定のデータを更新したい(UPDATE)

前回の Excel VBA で一時DBを使用する ― INSERTとSELECT では、
下記データをADOに登録しました。

[2014/10/01 7:40:58.112] RCV command=ACK ID=110405 messageId=001 data=
[2014/10/01 7:40:58.303] SND command=ACK ID=110405 messageId=002 data=
[2014/10/01 7:40:58.579] RCV command=DATA ID=110405 messageId=501 data=14B4 88B7 D54B A0D2 9A6A 3935


この記事ではADOに登録した内容にて、
特定のcommandとIDを持つレコードを更新したいと思います。


ここで早速問題が。


ADOではSQL文のWHERE句のように、
複数のカラムを選択条件に指定することができません!

なので、今回は選択(SELECT)時専用のカラムを用意する必要があります。

Public Sub OpenDb()
    Set adoRecordset = New ADODB.Recordset
    Call adoRecordset.Fields.Append("timestamp", ADODB.adDouble)
    Call adoRecordset.Fields.Append("direction", ADODB.adVarChar, 3)
    Call adoRecordset.Fields.Append("command", ADODB.adVarChar, 4)
    Call adoRecordset.Fields.Append("id", ADODB.adInteger)
    Call adoRecordset.Fields.Append("message_id", ADODB.adVarChar, 3)
    Call adoRecordset.Fields.Append("data", ADODB.adVarChar, 256)
    Call adoRecordset.Fields.Append("key", ADODB.adVarChar, 256)
    
    adoRecordset.Open
End Sub
Public Sub Insert( _
    ByVal timestamp As Double, _
    ByVal direction As String, _
    ByVal command As String, _
    ByVal id As Long, _
    ByVal messageId As String, _
    ByVal data As String)
    
    Call adoRecordset.AddNew '新しいレコードを登録
    
    adoRecordset.Fields("timestamp") = timestamp
    adoRecordset.Fields("direction") = direction
    adoRecordset.Fields("command") = command
    adoRecordset.Fields("id") = id
    adoRecordset.Fields("message_id") = messageId
    adoRecordset.Fields("data") = data
    adoRecordset.Fields("key") = command & id
    
    Call adoRecordset.Update '新しいレコードの内容を更新
End Sub

それぞれ9行目と17行目で、選択時専用のカラムを用意して
command と id をくっつけた値を登録してあげています。

□ 特定のデータを探して更新

前回作成した標準モジュールMyDBに、下記関数を追加します。

Public Sub UpdateWithSelectedData(ByVal command As String, ByVal data As String)
    If 0 = adoRecordset.RecordCount Then  '登録レコード数が 0 なら何もしない
        Exit Sub
    End If
    
    Dim criteria As String
    criteria = "command = '" & command & "'"  '"command = 'hogehoge'" になる
    
    adoRecordset.MoveFirst
    Call adoRecordset.Find(criteria, 0, adSearchForward)  '現在のレコードも含めて検索
    Do While Not adoRecordset.EOF
        adoRecordset.Fields("data") = data
        adoRecordset.Update  'レコードの内容を更新
        
        Call adoRecordset.Find(criteria, 1, adSearchForward)  '現在のレコードを含めないで検索
    Loop
End Sub

先頭から検索するため、9行目でカレントレコードを先頭に変更します。
続いて、ADOの Find() メソッドに検索条件を指定して、
該当するレコードをカレントレコードにセットします(10行目)。
最後まで検索しきるまで Find() を繰り返すことで、該当する全レコードを検索できます(15行目)。

ポイントは Find() の第2引数。
最初(10行目)はカレントレコードも検索対象にしたいので、
SkipRecords(検索開始オフセット位置)に 0 を指定します。

次からはカレントレコードを含めないで検索させたいので、
SkipRecordsに 1 を指定して、カレントレコードの次から検索させます。

なお、Find() には検索開始位置も指定できます(Start)。
これを使うと、上記とは異なった実装になります。

レコード内容の更新はレコード登録と同じやり方です。
カレントレコードのカラムの値を変更して、Update() でデータを更新します(13行目)。



「特定のデータを更新したい(UPDATE)」はここまでです。

次の記事では特定のデータを削除(DELETE)する方法をご紹介します(^^)/~

こんにちは。ひらのです。

Excel VBA で大量の文字列をいじったり並べ替えたりする際に、
いちいちシート上で計算すると、とっても遅くなってしまいます。

そんな時に便利な一時DB⇔ADOをご紹介します。

■前提知識

  • 簡単なExcel VBAコードを読めること
  • 簡単な正規表現が読めること(文字列解析の解説がいらない人は不要です)

■お題(要件)

  1. 任意のフォーマットの文字列を解釈したい(SELECT, INSERT)
  2. 特定のデータを更新したい(UPDATE)
  3. 特定のデータを削除したい(DELETE)

長くなってしまったので、それぞれ記事を分けてご紹介します。
この記事では「任意のフォーマットの文字列を解釈したい」を解説します。

■ADOを使用するための環境

ADOを使用するには"Microsoft ActiveX Data Objects"が必要です。

Excel VBAのウィンドウ(開発ツール)メニューバー[ツール] > [参照設定]を開いて、
"Microsoft ActiveX Data Objects X.X Library"にチェックを入れてください。
(Versionは何でもいいです。)

■任意のフォーマットの文字列を解釈したい

例として下記のようなデータを想定しましょう。

[2014/10/01 7:40:58.112] RCV command=ACK ID=110405 messageId=001 data=
[2014/10/01 7:40:58.303] SND command=ACK ID=110405 messageId=002 data=
[2014/10/01 7:40:58.579] RCV command=DATA ID=110405 messageId=501 data=14B4 88B7 D54B A0D2 9A6A 3935


この中から取り出したいデータは下記とします。

  1. 日時(年~ミリ秒)
  2. RCV/SNDの部分
  3. command
  4. ID
  5. messageId
  6. data

上記を下記のテーブル構成としましょう。

No項目カラム名サイズ
1日時timestampADODB.adDouble-
2RCV/SNDdirectionADODB.adVarChar3
3commandcommandADODB.adVarChar4
4IDidADODB.adInteger-
5messageIdmessage_idADODB.adVarChar3
6datadataADODB.adVarChar256


残念ながらExcelやADOにはミリ秒を扱える日付型は存在しないので、
日時はシリアル値で表現しようと思います。
日付型を使用する場合は ADODB.adDBTimeStamp を使用してください。
また、文字列はサイズを指定してあげる必要がありますので、サイズを決めておきましょう。

では、これをコードに落とします。
今回はDB用に標準モジュール"MyDB"をつくって、そこにコードを書きました。

Private adoRecordset As ADODB.Recordset


Public Sub OpenDb()
    Set adoRecordset = New ADODB.Recordset
    Call adoRecordset.Fields.Append("timestamp", ADODB.adDouble)
    Call adoRecordset.Fields.Append("direction", ADODB.adVarChar, 3)
    Call adoRecordset.Fields.Append("command", ADODB.adVarChar, 4)
    Call adoRecordset.Fields.Append("id", ADODB.adInteger)
    Call adoRecordset.Fields.Append("message_id", ADODB.adVarChar, 3)
    Call adoRecordset.Fields.Append("data", ADODB.adVarChar, 256)
    
    adoRecordset.Open
End Sub
どんなテーブル構成にするかは、DB(ADO)のOpen前に定義します。


Openがあるということは、Closeが必要ですね。
Closeは下記のように定義しました。

Public Sub CloseDb()
    adoRecordset.Close
    Set adoRecordset = Nothing
End Sub


次に、ファイルからデータを読み込んでADOに登録していきます。
が、今回扱うデータは時間をシリアル値に変換しなきゃいけなかったり、
区切り文字をスペースにするとdata部分がブチブチ切れそうだったり、面倒くさそうですね。
この文字列から欲しい情報を抽出する部分とADOへの登録は、後ほどご説明します。

では、まずはファイルを読み込むところから

Public Sub Execute()
    ' --- DBとテーブル作成 ---
    Call MyDb.OpenDb


    ' --- ファイルを読み込んでDBにINSERT ---
    Dim fileNo As Integer
    Dim line As String
    fileNo = FreeFile 'ファイル番号の取得
    Open ActiveWorkbook.Path & "\data.txt" For Input As #fileNo 'ファイルを読込モードで開く
    Do Until EOF(fileNo) 'EOFになるまでループ
        Line Input #fileNo, line 'ファイルから1行読み込む
        Call InsertLineToDb(line)
    Loop
    Close #fileNo
    
    Call MyDb.PrintAllRecord


    Call MyDb.CloseDb
End Sub

ポイントは 3,12,18行目です。
3行目でDB(ADO)を作成して、使い終わった18行目で閉じてあげます。
InsertLineToDb() は文字列を解析してADOに登録する自作関数です。
PrintAllRecord() はADOに登録した内容をイミディエイトウィンドウに出力する自作関数ですが、
後で解説します。

行単位でファイルを読み込む部分の解説は省いて、InsertLineToDb() の中身を見てみましょう。

Private Sub InsertLineToDb(ByVal str As String)
    Dim id As Long
    Dim timestamp As Date
    
    Dim regExpObj, matcher, submatches As Object
    Set regExpObj = CreateObject("VBScript.RegExp")
    With regExpObj
        .Pattern = "\[([0-9/\s:\.]+)\]\s([A-Z]{3})\scommand=([A-Z]+)\sID=([0-9]{6})\smessageId=([0-9]{3})\sdata=(.*)"
        .IgnoreCase = True
        .Global = True ' 文字列全体を検索
        
        Set matcher = .Execute(str)
        If 0 < matcher.Count Then
            Set submatches = matcher(0).submatches ' index は 0 固定
            
            timestamp = ToTimestamp(submatches(0))
            id = CLng(submatches(3))
            Call MyDb.Insert(timestamp, submatches(1), submatches(2), id, submatches(4), submatches(5))
        End If
    End With
    
    Set submatches = Nothing
    Set matcher = Nothing
    Set regExpObj = Nothing
End Sub

□ 文字列解析

文字列解析は正規表現を使用しています。
正規表現を使用するには正規表現オブジェクト"VBScript.RegExp"を使用します。

ここでのポイントは正規表現のグループ化。
括弧()で抽出したいパターンを囲むと、囲まれた部分にマッチする文字列だけ
順番に取得することができます。

簡単な例を挙げましょう。

文字列:ABcdExy34z
パターン:.+([a-z]+).+([0-9]+).+
→ Matcherのsubmatches 0番目に "cd"、1番目に "34" が入る


□ シリアル値への変換

16行目の自作関数 ToTimestamp()で読み込んだ文字列(例:2014/10/01 7:40:58.112)を
シリアル値に変換しています。
シリアル値は、1900/1/1を1として、1日経過するごとに1を増やす日時の表現方法です。

ToTimestamp() の実装はこんな感じです。

Private Function ToTimestamp(ByVal str As String) As Double
    Dim ms As Double
    Dim d As Date
    
    Dim regExpObj, matcher, submatches As Object
    Set regExpObj = CreateObject("VBScript.RegExp")
    With regExpObj
        .Pattern = "([0-9/\s:]+)\.([0-9]{3})"
        .IgnoreCase = True
        .Global = True ' 文字列全体を検索
        
        Set matcher = .Execute(str)
        If 0 < matcher.Count Then
            Set submatches = matcher(0).submatches
            d = CDate(submatches(0))
            ms = CDbl(submatches(1))


            ToTimestamp = d + (ms / 24 / 60 / 60 / 1000) ' ミリ秒を24時間÷60分÷60秒÷1000ミリ秒 でシリアル値に変換
        End If
    End With
    
    Set submatches = Nothing
    Set matcher = Nothing
    Set regExpObj = Nothing
End Function

日時の文字列を正規表現で"年~秒"と"ミリ秒"部分に分けています。
文字列の長さが固定の場合はLeft$()やRight$()などを使えますが、
月や時間などの長さが不定(例:3/4, 11/12)なので、正規表現を使いました。

18行目でシリアル値で計算したいため、15行目では取得した"年~秒"をDate型に、
16行目では"ミリ秒"をDouble型に変換しています。
Date型にするとシリアル値で表現されるので、そのままシリアル値として計算できます。

□ ADOへのレコード登録(INSERT)

やっと本題のDB(ADO)へのレコード登録に来ました(長かった...)。

レコード登録は自作関数 MyDb.Insert() で下記のように組んでいます。

Public Sub Insert( _
    ByVal timestamp As Double, _
    ByVal direction As String, _
    ByVal command As String, _
    ByVal id As Long, _
    ByVal messageId As String, _
    ByVal data As String)
    
    Call adoRecordset.AddNew '新しいレコードを登録
    
    adoRecordset.Fields("timestamp") = timestamp
    adoRecordset.Fields("direction") = direction
    adoRecordset.Fields("command") = command
    adoRecordset.Fields("id") = id
    adoRecordset.Fields("message_id") = messageId
    adoRecordset.Fields("data") = data
    adoRecordset.Fields("key") = command & id
    
    Call adoRecordset.Update '新しいレコードの内容を更新
End Sub

まずは新しいレコードを登録します(9行目)。
そのあと、Open時に指定してあげたカラム名を Fields() に指定してデータを入れ(11~17行目)、
レコードを更新します(19行目)。
MySQLなどのRDBMSみたいに、値を指定しつつレコード登録はできないのですね。

□ ADOレコードの参照(SELECT)

コード上でデータをDB(ADO)に登録していても、本当に登録されているか不安です。
そんな時はADOの内容をイミディエイトウィンドウに出力して、内容を確認してみましょう。
(イミディエイトウィンドウはメニューバー[表示] > [イミディエイト ウィンドウ]で表示できます。)

以下はADO内の全レコードの内容を出力するコードです。

Public Sub PrintAllRecord()
    Dim i As Integer
    Dim str As String
    
    adoRecordset.MoveFirst    '先頭のレコードにカレントレコードを移動
    Do Until adoRecordset.EOF
        str = ""
        For i = 0 To adoRecordset.Fields.Count - 1
            str = str & adoRecordset.Fields(i) & " "
        Next i
        Debug.Print str
        adoRecordset.MoveNext '次のレコードにカレントレコードを移動
    Loop
End Sub

ADOでは"カレントレコード"を操作して、操作対象のレコードを設定します。
ファイルポインタみたいなものですね。

ファイルの操作時と同じように、EOFになるまでカレントレコードを移動させてあげます。
カレントレコードを移動させるメソッドには、下記があります。

MoveFirst:最初のレコードに移動
MoveLast:最後のレコードに移動
MovePrevious:前のレコードに移動
MoveNext:次のレコードに移動



「任意のフォーマットの文字列を解釈したい」はここまでです。

次の記事では特定のデータを更新(UPDATE)する方法をご紹介します(^^)/~