[2018/05/18] 処理時間の計測(Stopwatchクラス) (No.43)
[2018/03/23] バイト配列を設定バイト値で埋める (No.35)
[2018/03/22] 日付型データの使い方 (No.34)
[2018/03/22] スレッドタイマの使い方(イベント処理に時間が掛かる場合) (No.33)
-
文字列から数値型への変換はいろんな所で必要になりますが、例えばデータベースからのデータの取得で 結果を文字列にして読込んだ場合、これを数値にする場合などです。
数値変換には各データ型の Parse メソッドを利用して可能です。以下に例として Integer型 の変換関数を示します。文字列からInteger型への変換
'''
''' 文字列からInteger型にデータ変換 ''' ''' <param name="strVal">元の文字列</param> '''変換後データ Function StringToInt(ByVal strVal As String) As Integer Try Return Integer.Parse(strVal) '「Convert」クラスの関数を利用しても同じです 'Return Convert.ToInt32(strVal) Catch ex As Exception '例外発生の場合、「0」で返す Return 0 End Try End FunctionParse メソッドでの例外が発生した場合には全て0で返す様にしています。 データベースからのデータ取得で文字列から数値に変換する場合、ほとんど例外エラーは発生しないのでこの様にしています。 データベースの数値カラムからのデータで数値以外はあり得ないと思いますので。
但し、この関数ですが以下の様な使い方をすると非常に時間が掛かる様です。 (私のPC上では10秒以上の時間が掛かりました。)
文字列から数値型への変換の実行例1
Private Sub StrToInt_Click(sender As Object, e As EventArgs) Handles StrToInt.Click '変換が問題無い例 Dim strValue As String = "123456789" Dim intValue As Integer = 0 intValue = StringToInt(strValue) MsgBox("Integer変換:" & intValue.ToString) '時間計測を「Stopwatch」クラスで行う Dim stopwatch As New Stopwatch() stopwatch.Start() strValue = "ABCD" For i As Integer = 1 To 1000 '関数内でExceptionが発生するはず intValue = StringToInt(strValue) Next stopwatch.Stop() Dim str As String = "" str &= String.Format("ミリ秒単位の経過時間の合計 = {0}", stopwatch.ElapsedMilliseconds) MsgBox(str) End SubVB.NETの Exception 処理は非常に時間が掛かる処理の様で、今回の様な関数の呼び出しを多く行う場合には注意が必要です。 対策としては Exception 処理をなるべくさせないか、その他の方法を取る必要があります。
今回の対策としては Parse メソッドを TryParse メソッドに変更しています。
文字列からInteger型への変換・TryParse
'''
''' 文字列からInteger型にデータ変換その2 ''' ''' <param name="strVal">元の文字列</param> '''変換後データ Function StringToInt2(ByVal strVal As String) As Integer Try Dim intVal As Integer 'TryParseでInteger型に変換 Dim result As Boolean = Int32.TryParse(strVal, intVal) 'ParseでInteger型に変換 Return intVal Catch ex As Exception '例外発生の場合、「0」で返す Return 0 End Try End FunctionTryParse メソッドは変換結果を第2引数に返し、戻り値は True を返します。 尚、失敗した場合は第2引数に0を返し、戻り値は False を返します。
先ほどの使用例をこの関数を使う様に変更すれば、処理時間は格段に向上します。
とにかくネストが深い処理等で処理時間が異常に長い場合には、Exception 処理が絡んでいないかを疑ってみては如何でしょうか。
文字列から数値型への変換の実行例2
Private Sub StrToInt_Click(sender As Object, e As EventArgs) Handles StrToInt.Click '時間計測を「Stopwatch」クラスで行う Dim stopwatch As New Stopwatch() stopwatch.Start() strValue = "ABCD" For i As Integer = 1 To 1000 '呼び出し関数を変更 intValue = StringToInt2(strValue) Next stopwatch.Stop() Dim str As String = "" str &= String.Format("ミリ秒単位の経過時間の合計 = {0}", stopwatch.ElapsedMilliseconds) MsgBox(str) End Sub関連する記事
⇒オブジェクト型から数値型への変換(TryParse)
⇒文字列定数(改行、タブ、バックスペース等)について
⇒文字列変換関数(StrConv)で変換が途中で切れる
⇒文字列変換関数(StrConv)の使い方
PR -
処理時間の短縮を図る場合に、処理にどのくらいの時間が掛かっているのかを計測する場合があります。
基本的には秒単位での時間計測で問題無い場合は、計測対象の処理の前で時刻を Now() で取得し、 処理終了後に再度 Now() で取得した時刻の差を求めることで計測ができます。処理時間の計測その1
Private Sub TimeMeasure1_Click(sender As Object, e As EventArgs) Handles TimeMeasure1.Click '開始時刻退避 Dim start As DateTime = Now '各種の処理の代わりとしての待ち時間(10秒) System.Threading.Thread.Sleep(10000) '処理時間を計算(現在時刻-開始時刻) Dim timespan As TimeSpan = Now - start MsgBox(String.Format("処理時間 = {0}秒", timespan.TotalSeconds)) End Sub更に、高精度な時間を計測したい場合には、 Stopwatch クラスを用います。
以下のソースでは、 Stopwatch クラス生成後、計測対象処理の直前で Start メソッドで計測開始し、 計測対象処理の直後で Stop メソッドで計測終了します。
処理時間の計測その2
Private Sub TimeMeasure2_Click(sender As Object, e As EventArgs) Handles TimeMeasure2.Click '時間計測を「Stopwatch」クラスで行う Dim stopwatch As New Stopwatch() '計測を開始 stopwatch.Start() '各種の処理の代わりとしての待ち時間(10秒) System.Threading.Thread.Sleep(10000) '計測を終了 stopwatch.Stop() Dim str As String = "" str &= String.Format("経過時間の合計 = {0}", stopwatch.Elapsed) & vbCrLf str &= String.Format("ミリ秒単位の経過時間の合計 = {0}", stopwatch.ElapsedMilliseconds) MsgBox(str) End Sub関連する記事
⇒スレッドタイマの使い方 :[System.Threading.Timer,Delegate,Invoke]
⇒スレッドタイマの使い方(イベント処理が時間が掛かる場合):[System.Threading.Timer,Delegate,Invoke]
⇒引数があるスレッドの実行について(パラメータ付きスレッド)
⇒スレッドを停止させる方法について
-
指定されたバイト配列を、指定値で埋める関数です。バイト配列などは通常は使用しないのですが、制御関係などでは使用することもあり、 初期値として、バイト配列を設定する必要があります。
関数として以下に2種類の関数を例に示していますが、最初は単純に配列1個ずつに指定バイト値を設定しているだけです。 関数にするほどでもないのですが、この関数を元にしてInteger型の配列やDouble型の配列の初期化用の関数をオーバーライドすることができます。
2つ目の関数は、System.Linq の Enumerable を用いた方法を示します。 Enumerable.Repeat で IEnumerable なオブジェクトを生成し、更にそれをArray化して、コピーを行っています。 (こちらの方が、見やすいかもしれません。)バイト配列を設定バイト値で埋める
''' ----------------------------------------------------------------------------- '''''' バイト配列を設定値で埋める ''' ''' <param name="desArr">対象バイト配列</param> ''' <param name="fillByte">バイト値</param> '''単純に配列を1個ずつ処理 ''' ----------------------------------------------------------------------------- Public Sub FillByteArr(ByVal desArr As Byte(), ByVal fillByte As Byte) If desArr.Length > 0 Then Dim idx As Integer For idx = 0 To desArr.Length - 1 desArr(idx) = fillByte Next End If End Sub ''' ----------------------------------------------------------------------------- '''''' バイト配列を設定値で埋める・その2 ''' ''' <param name="desArr">対象バイト配列</param> ''' <param name="fillByte">バイト値</param> '''System.Linq の Enumerable を用いた方法 ''' ----------------------------------------------------------------------------- Public Sub FillByteArr2(ByVal desArr As Byte(), ByVal fillByte As Byte) If desArr.Length > 0 Then Dim arr As IEnumerable(Of Byte) = Enumerable.Repeat(fillByte, desArr.Length) Array.Copy(arr.ToArray(), desArr, desArr.Length) End If End Sub '***** '仮にテストプログラム '***** Private Sub FillByte_Click(sender As Object, e As EventArgs) Handles FillByte.Click Try '配列宣言 Dim byteArr(10) As Byte '配列を埋める FillByteArr2(byteArr, &H55) '結果の表示 Dim str As String = "" For i = 0 To byteArr.Length - 1 str &= String.Format("byteArr[{0}] = {1}", i, byteArr(i).ToString) & vbCrLf Next MsgBox(str) Catch ex As Exception 'エラー MsgBox(ex.Message) End Try End Sub関連する記事
⇒左詰でバイト配列のコピーを行う
⇒右詰でバイト配列のコピーを行う
-
データ型の中で 日付型(Date) はよく使用するもののひとつです。 VB.NETの中に日付のデータ型として、 Date と DateTime がありますが、 どの様な違いがあるのか疑問に思われるでしょうが、以下のMSDNの説明によれば同じものの様です。
⇒データ型の概要 (Visual Basic)
日付型として同じであれば、どちらを使ってもよいのですが、プログラムの中では一つに統一した方が良いと思います。 (私としては DateTime で良いと思います)
以下のソースは抜粋ですが、どちらも同じことをを処理しています。日付型データの使い方1
Dim strWK As String = "" Dim dateWK As Date 'Date型 dateWK = New Date(2018, 1, 20, 15, 17, 30) strWK = dateWK.ToString("yyyy/MM/dd HH:mm:ss") '日付型を書式で変換 Console.WriteLine("dateWK.ToString(""yyyy/MM/dd HH:mm:ss"") = " & strWK) 'DateTime型 Dim dateTimeWK As DateTime dateTimeWK = New DateTime(2018, 1, 20, 15, 17, 30) strWK = dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss") Console.WriteLine("dateTimeWK.ToString(""yyyy/MM/dd HH:mm:ss"") = " & strWK)ソースの中で、ToString を使用して日付型データから書式に従って文字列に変換しています。 今回の例では書式を"yyyy/MM/dd HH:mm:ss"を使っていますが、通常はこの様な感じで十分かと思います。
尚、この書式には各種ありますが代表的なものを以下に示します。書式指定子 説明 使用例 yy 0埋め下2桁の年 (00 ~ 99) 2009-06-01 13:45:30 ⇒ "09" yyyy 0埋め4桁の年 (0001 ~ 9999) 2009-06-01 13:45:30 ⇒ "2009" M 月 (1 ~ 12) 2009-06-01 13:45:30 ⇒ "6" MM 月:1桁の場合先頭0付 (01 ~ 12) 2009-06-01 13:45:30 ⇒ "06" d 月の日にち (1 ~ 31) 2009-06-01 13:45:30 ⇒ "1" dd 月の日にち:1桁の場合先頭0付 (01 ~ 31) 2009-06-01 13:45:30 ⇒ "01" h 12時間形式の時間 (1 ~ 12) 2009-06-01 13:45:30 ⇒ "1" hh 12時間形式の時間:1桁の場合先頭0付 (01 ~ 12) 2009-06-01 13:45:30 ⇒ "1" H 24時間形式の時間 (0 ~ 23) 2009-06-01 03:45:30 ⇒ "3" HH 24時間形式の時間:1桁の場合先頭0付 (00 ~ 23) 2009-06-01 13:45:30 ⇒ "13" mm 分:1桁の場合先頭0付 (00 ~ 59) 2009-06-01 13:05:30 ⇒ "05" ss 秒:1桁の場合先頭0付 (00 ~ 59) 2009-06-01 13:05:04 ⇒ "04" ddd 曜日の省略名 2009-06-01 13:45:30 ⇒ "月" dddd 曜日の完全名 2009-06-01 13:45:30 ⇒ "月曜日"
次に日付型の計算について説明します。 いろんなところで日付に対して翌日、前日、前月、翌月、前年、翌年などの計算が必要になりますが、 日付型のメソッドとしてそれらを計算できるものが揃っています。 私がよく使うものを以下に示します。
メソッド 書式 説明 AddDays AddDays (value As Double) As DateTime
value 引数は、正または負のどちらの場合もあります指定された日数を加算した新しい DateTime を返します AddMonths AddMonths (value As Integer) As DateTime
value 引数は、正または負のどちらの場合もあります指定された月数を加算した新しい DateTime を返します AddYears AddYears (value As Integer) As DateTime
value 引数は、正または負のどちらの場合もあります指定された年数を加算した新しい DateTime を返します
日付型データの使い方2
'日付の設定 dateTimeWK = New DateTime(2018, 1, 25) '日数+1 dateTimeWK = dateTimeWK.AddDays(1) Console.WriteLine("日数+1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '日数-1 dateTimeWK = dateTimeWK.AddDays(-1) Console.WriteLine("日数-1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '月数+1 dateTimeWK = dateTimeWK.AddMonths(1) Console.WriteLine("月数+1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '月数-1 dateTimeWK = dateTimeWK.AddMonths(-1) Console.WriteLine("月数-1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '年数+1 dateTimeWK = dateTimeWK.AddYears(1) Console.WriteLine("年数+1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '年数-1 dateTimeWK = dateTimeWK.AddYears(-1) Console.WriteLine("年数-1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss"))上記の処理を実行すると以下の様な表示が、「出力」ウインドウに表示されます。
dateWK.ToString("yyyy/MM/dd HH:mm:ss") = 2018/01/20 15:17:30 dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss") = 2018/01/20 15:17:30 日数+1 = 2018/01/26 00:00:00 日数-1 = 2018/01/25 00:00:00 月数+1 = 2018/02/25 00:00:00 月数-1 = 2018/01/25 00:00:00 年数+1 = 2019/01/25 00:00:00 年数-1 = 2018/01/25 00:00:00
関連する記事
⇒日付型データの使い方(月末、年末の日付取得)
⇒日付型データの比較について
⇒文字列から数値型への変換(parse - tryparse)
⇒オブジェクト型から数値型への変換(TryParse)
-
スレッドタイマとしては以下のページに説明をしましたが、
⇒スレッドタイマの使い方
この時のタイマイベントの処理は、単に時刻を表示するだけの処理で時間がほとんど掛からないものでした。 しかし、実際のプログラムではそのようなことはなく、処理時間が結構かかるものが在ると思います。
そこで今回は、タイマイベントの処理の中に時間が掛かる処理のシミュレーションとして Sleep関数で3秒待たせることにしました。 この関数の呼び出しの前後で時刻表示を入れましたので、何時の時点での処理なのかが分かります。 実際のプログラムは以下の様になります。スレッドタイマの使い方(イベント処理が時間が掛かる場合)
Imports System.Threading Imports System.Threading.Thread Public Class frmTimerTh2 'スレッドタイマ Private mtimer As System.Threading.Timer '''''' フォームロードイベント ''' Private Sub frmTimerThMul_Load(sender As Object, e As EventArgs) Handles Me.Load 'タイマコールバック関数 Dim timerDelegate As TimerCallback = New TimerCallback(AddressOf TimerEvent) '1000msecタイマ生成(コールバック関数の設定) mtimer = New Timer(timerDelegate, Nothing, 0, 1000) End Sub '''''' System.Threading.Timer からの呼び出しを処理するメソッド ''' ''' <param name="state">このデリゲートで呼び出されたメソッドに関連するアプリケーション固有の情報を格納するオブジェクト</param> Private Sub TimerEvent(ByVal state As Object) 'テキストボックスに現在時刻表示 Dim strDate As String = Now.ToString("hh:mm:ss") Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate}) '3秒の待ち Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate & "⇒Start...5秒の待ち"}) Sleep(3 * 1000) Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate & "⇒End.....5秒の待ち"}) End Sub ' ラベル表示デリゲート宣言 Private Delegate Sub SetLabelTextDelegate(ByVal strText As String) '''''' ラベル表示 ''' ''' <param name="strText">表示文字列</param> Private Sub SetLabelText(ByVal strText As String) Me.TextBox1.Text &= strText & vbCrLf End Sub End Classこれを実行させると、以下の様な表示なります。
最初、4個の「Start...5秒の待ち」が続いてその後で「End.....5秒の待ち」が追いかけてくる感じになっています。
これは、スレッドタイマのイベントの時間間隔が1秒なので、先に発生したイベントを待たずに 次々とイベントが発生させていることが分かります。 つまり、各1秒毎のイベントは別々に実行していることになります。
3秒待ちの処理が重なって処理されては困ることになります。 そこで、以下のプログラムの様に、イベント処理中のフラグを用いて処理をスキップさせてみました。スレッドタイマの使い方(イベント処理が時間が掛かる場合:改良版)
Imports System.Threading Imports System.Threading.Thread Public Class frmTimerTh2 'スレッドタイマ Private mtimer As System.Threading.Timer '''''' フォームロードイベント ''' Private Sub frmTimerThMul_Load(sender As Object, e As EventArgs) Handles Me.Load 'タイマコールバック関数 Dim timerDelegate As TimerCallback = New TimerCallback(AddressOf TimerEvent) '1000msecタイマ生成(コールバック関数の設定) mtimer = New Timer(timerDelegate, Nothing, 0, 1000) End Sub '''''' System.Threading.Timer からの呼び出しを処理するメソッド ''' ''' <param name="state">このデリゲートで呼び出されたメソッドに関連するアプリケーション固有の情報を格納するオブジェクト</param> Private Sub TimerEvent(ByVal state As Object) '処理中フラグ Static fProcessing As Boolean = False '処理中フラグOFFのチェック If fProcessing = False Then '処理中フラグON fProcessing = True 'テキストボックスに現在時刻表示 Dim strDate As String = Now.ToString("hh:mm:ss") Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate}) '3秒の待ち Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate & "⇒Start...5秒の待ち"}) Sleep(3 * 1000) Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate & "⇒End.....5秒の待ち"}) '処理中フラグOFF fProcessing = False End If End Sub ' ラベル表示デリゲート宣言 Private Delegate Sub SetLabelTextDelegate(ByVal strText As String) '''''' ラベル表示 ''' ''' <param name="strText">表示文字列</param> Private Sub SetLabelText(ByVal strText As String) Me.TextBox1.Text &= strText & vbCrLf End Sub End Classこのプログラムの実行は以下の様になり、イベントの多重呼び出しは無くなりました。
関連する記事
⇒Timerコントロールの使い方 :[Timer,Interval]
⇒複数のTimerコントロールの使い方 :[Timer,Interval]
⇒スレッドタイマの使い方 :[System.Threading.Timer,Delegate,Invoke]