[2019/08/30] 配列の使い方の注意点について(コピー, Clone) (No.119)
[2019/08/29] 配列の使い方について(Dim, Redim) (No.118)
[2019/08/20] バイト指定による文字列の切出しに付いて(MidB, LeftB, RightB) (No.115)
[2019/08/02] 文字列の分解に付いて(String.Split) (No.113)
[2019/06/20] EXCELファイルはXMLファイルの集まり(ZIPファイル)? (No.106)
-
×
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
-
前回の以下の記事は配列の使い方の基本について記しましたが、 今回は配列を使う場合の注意点をまとめてみました。
⇒配列の使い方について(Dim, Redim)
それでは以下の内容で説明したいと思います。■配列変数のコピー
以下のソースを見て下さい。 最初に配列「arr1」「arr2」を宣言していますが、「arr1」のみ初期化も行っています。
その後、「arr2」に「arr1」を代入しています。 (この代入で「arr2」に「arr1」の内容全てがコピーされた様に思いますが、実際にそうでしょうか。)
「arr1」の2番目の要素クリア後、「arr1」及び「arr2」の内容を全てコンソールに表示させてどうなったかを見てみます。Private Sub btnArray_Click(sender As Object, e As EventArgs) Handles btnArray.Click '配列宣言 Dim arr1() As Integer = {100, 200, 300} 'こちらは配列初期化有り Dim arr2() As Integer '配列宣言のみ '配列変数のコピー arr2 = arr1 '「arr1」の2番目の要素クリア arr1(1) = 0 '「arr1」の内容表示 For i As Integer = 0 To UBound(arr1) Console.WriteLine("arr1[{0}]:{1}", i, arr1(i)) Next '「arr2」の内容表示 For i As Integer = 0 To UBound(arr2) Console.WriteLine("arr2[{0}]:{1}", i, arr2(i)) Next End Sub
実行結果がコンソールには以下様に表示されます。
arr1[0]:100 arr1[1]:0 arr1[2]:300 arr2[0]:100 arr2[1]:0 arr2[2]:300
予想に反して「arr1」及び「arr2」共に同じ値が表示されました。
どうしてこうなるのかと言いますと、「配列変数のコピー」では配列の中身では無く参照をコピーすることになるからです。 参照とは配列の実体を指し示すポインタと言った方がいいかもしれません。
実は変数「arr1」も配列の実体の参照でしかないということです。 {100, 200, 300} と宣言された配列の実体はどこかにあり、その実体を指し示すポインタを変数「arr1」が保持しています。
実体をコピーしたい場合には配列の Clone メソッドを使います。以下にその様子を示します。
■配列変数の実体コピー(Clone)
上記のソースでは配列変数コピーで単に変数同士のコピーを行いましたが、 以下では配列の Clone メソッドの結果を代入しています。
Private Sub btnArray_Click(sender As Object, e As EventArgs) Handles btnArray.Click '配列宣言 Dim arr1() As Integer = {100, 200, 300} 'こちらは配列初期化有り Dim arr2() As Integer '配列宣言のみ '配列変数の実体コピー arr2 = arr1.Clone '「arr1」の2番目の要素クリア arr1(1) = 0 '「arr1」の内容表示 For i As Integer = 0 To UBound(arr1) Console.WriteLine("arr1[{0}]:{1}", i, arr1(i)) Next '「arr2」の内容表示 For i As Integer = 0 To UBound(arr2) Console.WriteLine("arr2[{0}]:{1}", i, arr2(i)) Next End Sub
実行結果がコンソールには以下様に表示されます。
arr1[0]:100 arr1[1]:0 arr1[2]:300 arr2[0]:100 arr2[1]:200 arr2[2]:300
結果を見れば指標「1」の要素が「arr1」及び「arr2」が異なる値であることが分かります。
Clone メソッドは配列の実体を別に作成し、全ての要素をコピーしその参照を返します。 (Clone 直後は要素の内容は全て同じです。) そのため、「arr1」及び「arr2」は実体が別々の配列となります。
■配列の実体コピー・ディープコピー(Serializable)
以下の記事にもありますが、配列も1個のオブジェクトなので配列の実体をシリアル化して、別の実体にシリアル化を戻せば、 配列のコピーができることになります。
⇒各種オブジェクトのコピーができるディープコピー(BinaryFormatter,MemoryStream,Serialize,Deserialize)
以下のソースに上記の記事内の関数を利用して配列のコピーを行います。Private Sub btnArray_Click(sender As Object, e As EventArgs) Handles btnArray.Click '配列宣言 Dim arr1() As Integer = {100, 200, 300} 'こちらは配列初期化有り Dim arr2() As Integer '配列宣言のみ '配列の実体コピー・ディープコピー arr2 = DeepCopy(arr1) '「arr1」の2番目の要素クリア arr1(1) = 0 '「arr1」の内容表示 For i As Integer = 0 To UBound(arr1) Console.WriteLine("arr1[{0}]:{1}", i, arr1(i)) Next '「arr2」の内容表示 For i As Integer = 0 To UBound(arr2) Console.WriteLine("arr2[{0}]:{1}", i, arr2(i)) Next End Sub
実行結果がコンソールには以下様に表示されます。
arr1[0]:100 arr1[1]:0 arr1[2]:300 arr2[0]:100 arr2[1]:200 arr2[2]:300
関連する記事
⇒配列の使い方について(Dim, Redim)
⇒配列の範囲指定によるコピー(Array.Copy, Skip, Take)
PR -
配列は似た性質のデータをまとめて取り扱えるようにするデータ形式です。 配列の機能は、その昔の言語である FORTRAN , C言語 (私も使った言語ですが)等でも持っています。 ここまで配列の機能が残っているということは、それなりに使い勝手が良いのではないかと思います。 そこで今回は、配列の使い方について順を追って説明したいと思います。
■簡単な配列の宣言と使い方
以下のソースを見て下さい。 処理の前半は、配列を利用しない場合の合計処理を示しています。 配列が無い場合は、値を別々の変数に宣言して、それぞれを参照し合計を求めています。 配列を利用しないと非常に煩雑になってしまします。
処理の後半で配列を利用する場合を示しています。 配列の各要素に値を設定する部分は、ベタに記述していますが、合計を求めるところで 配列の処理らしさが現れています。 (この処理などは初歩の初歩なので特に難しくないと思います。)Private Sub btnArray_Click(sender As Object, e As EventArgs) Handles btnArray.Click '===配列を利用しない場合の合計処理=== '各変数の宣言 Dim nVal1 As Integer Dim nVal2 As Integer Dim nVal3 As Integer Dim nVal4 As Integer Dim nVal5 As Integer '値の設定 nVal1 = 1 nVal2 = 10 nVal3 = 100 nVal4 = 1000 nVal5 = 10000 '値の合計 Dim nSum As Integer nSum = nVal1 + nVal2 + nVal3 + nVal4 + nVal5 Console.WriteLine("配列を利用しない場合の合計処理:{0}", nSum) '===配列を利用=== '配列宣言 Dim arr(5) As Integer '要素に値設定 arr(1) = 1 arr(2) = 10 arr(3) = 100 arr(4) = 1000 arr(5) = 10000 '指標宣言 Dim i As Integer '合計計算 nSum = 0 For i = 1 To 5 nSum = nSum + arr(i) Next Console.WriteLine("配列を利用した場合の合計処理:{0}", nSum) End Sub
実行結果がコンソールには以下様に表示されます。(当然合計値は同じ結果)
配列を利用しない場合の合計処理:11111 配列を利用した場合の合計処理:11111
■配列宣言の落とし穴
上記のソースでは配列宣言で Dim arr(5) As Integer と宣言しましたが、 領域としては指標が「1」~「5」まで確保されたかの様に感じますが、実は指標「0」の要素が存在します。
そこで上記のソースを以下の様に変更し実行してみます。Private Sub btnArray_Click(sender As Object, e As EventArgs) Handles btnArray.Click '===配列を利用=== '配列宣言 Dim arr(5) As Integer '要素に値設定 arr(1) = 1 arr(2) = 10 arr(3) = 100 arr(4) = 1000 arr(5) = 10000 arr(0) = 10000 '指標「0」の要素 '指標宣言 Dim i As Integer '合計計算 nSum = 0 For i = 0 To 5 nSum = nSum + arr(i) Next Console.WriteLine("配列を利用した場合の合計処理:{0}", nSum) End Sub
配列を利用した場合の合計処理:21111
結果を見れば指標「0」の要素が存在することが分かります。
Dim arr(5) で5個の領域を宣言したつもりが指標「0」の要素が存在するため、 全体では6個の領域を宣言したことになります。
配列の開始が指標「0」と考えるのか、それとも指標「1」からとするのかで、プログラムの動作や組み方が変わってきます。 複数の人で組む場合にはなおさらで、どちらにするのかを決めておく必要があります。
配列の開始が指標「0」を使うのであれば、必要個数の1個少な目で配列宣言するのか、 それとも指標「1」ならば必要個数で配列宣言し、指標「0」の要素は未使用とするか、と言ったところです。
■配列の初期化
配列の宣言で一緒に初期化ができます。 配列の要素数を記述せずに、宣言のデータ型の後に、=(イコール) で繋ぎ {}(中括弧) で要素を ,(カンマ) で区切って記述します。
上記のソースを配列の初期化を行う様に変更します。Private Sub btnArray_Click(sender As Object, e As EventArgs) Handles btnArray.Click '配列宣言(初期化) Dim arr() As Integer = {10000, 10000, 1000, 100, 10, 1} '指標宣言 Dim i As Integer '合計計算 Dim nSum As Integer nSum = 0 For i = 0 To 5 nSum = nSum + arr(i) Next Console.WriteLine("配列宣言(初期化)を利用した場合の合計処理:{0}", nSum) End Sub
このソース合計計算の For ループで「5」を直値(リテラル)で記述しています。 この場合は「5」であることが明らかなので問題は無いのですが、 配列の最終の指標が何か分からない時には UBound 等を用います。 以下に項目を新しくして説明します。
■配列の最終指標はUBound
UBound 関数は配列内の指定された次元における最も大きいインデックスを返します。
Public Function UBound(Array As System.Array, Optional Rank As Integer = 1) As Integer 引数: ・Array:任意のデータ型の配列です。 ある次元で最も大きいインデックスを探す対象となる配列です。 ・Rank :省略可能です。 Integer. 最も大きいインデックスが返される次元です。 1番目の次元の場合は「1」、2番目の次元の場合は「2」という形で指定します。 Rank を省略した場合、「1」が使用されます。 戻り値:Integer型 ・指定した次元に設定できるインデックスの最大値です。 ・Array に要素が 1 つしかない場合、UBound は 0 を返します。 ・Array に要素が存在しない場合 (長さ 0 の文字列の場合など)、UBound は -1 を返します。
以下の例では For ループの最終指標にこの UBound 関数を使っています。
尚、配列のプロパティのには現在の配列の大きさ(長さ)を返してくれる Length がありますのでそれを使っても同様です。 但し、 Length は個数なので「-1」する必要があります。Private Sub btnArray_Click(sender As Object, e As EventArgs) Handles btnArray.Click '配列宣言(初期化) Dim arr() As Integer = {10000, 10000, 1000, 100, 10, 1} '指標宣言 Dim i As Integer '合計計算 Dim nSum As Integer = 0 '[Ubound]を使った最終指標 For i = 0 To UBound(arr) nSum = nSum + arr(i) Next Console.WriteLine("配列宣言(初期化)を利用した場合の合計処理:{0}", nSum) nSum = 0 '配列の大きさを返すプロパティ[Length]を使った最終指標 For i = 0 To arr.Length - 1 nSum = nSum + arr(i) Next Console.WriteLine("配列宣言(初期化)を利用した場合の合計処理:{0}", nSum) End Sub
■配列のサイズ変更(ReDim)
配列のサイズを変更するために ReDim 命令を使います。
以下のソースでは ReDim 命令で指標が「2」までのサイズに縮めています。 (合計の結果は「21000」を想定していますが...)Private Sub btnArray_Click(sender As Object, e As EventArgs) Handles btnArray.Click '配列宣言(初期化:要素6個) Dim arr() As Integer = {10000, 10000, 1000, 100, 10, 1} '配列サイズ変更 ReDim arr(2) '合計計算 Dim nSum As Integer = 0 '[Ubound]を使った最終指標 For i As Integer = 0 To UBound(arr) nSum = nSum + arr(i) Next Console.WriteLine("配列をReDimでサイズ変更した場合の合計処理:{0}", nSum) End Sub
しかし結果は「0」となってしまいます。
配列をReDimでサイズ変更した場合の合計処理:0
これは ReDim 命令はサイズを変更した後で全ての要素をその配列のデータ型で初期化してしまうからです。 既に存在した要素を引き継ぐ場合には ReDim 命令で Preserve を指定します。
Private Sub btnArray_Click(sender As Object, e As EventArgs) Handles btnArray.Click '配列宣言(初期化:要素6個) Dim arr() As Integer = {10000, 10000, 1000, 100, 10, 1} '配列サイズ変更(Preserve指定) ReDim Preserve arr(2) '合計計算 Dim nSum As Integer = 0 '[Ubound]を使った最終指標 For i As Integer = 0 To UBound(arr) nSum = nSum + arr(i) Next Console.WriteLine("配列をReDimでサイズ変更した場合の合計処理:{0}", nSum) End Sub
結果は想定通り「21000」となりました。
配列をReDimでサイズ変更した場合の合計処理:21000
■配列のサイズ未指定での宣言
配列のサイズを指定せずに配列を宣言することができます。 この場合、配列は空の状態で存在し ReDim 命令でサイズを増やしてやらないと使用できません。 (空の状態の状態は Is Nothing で判定できます)
それでは、以下にInteger型配列をサイズ無しで宣言し、配列のサイズを順次拡張して中身が「0」~「9」となる 配列を作成する例を示します。Private Sub btnArray_Click(sender As Object, e As EventArgs) Handles btnArray.Click '配列宣言 Dim arr() As Integer ' = Nothing はあった方がいいと思います '「0」~「9」の処理 For i As Integer = 0 To 9 If arr Is Nothing Then '最初は指標「0」 ReDim arr(0) arr(0) = i Else '次の指標 Dim idx As Integer = UBound(arr) + 1 'サイズ拡張 ReDim Preserve arr(idx) '要素設定 arr(idx) = i End If Next '配列の要素を文字列として連結 Dim str As String = "" For i As Integer = 0 To UBound(arr) If str <> "" Then str &= "," str &= arr(i) Next Console.WriteLine("配列:{0}", str) End Sub
配列:0,1,2,3,4,5,6,7,8,9
■配列のループ処理は For Each
上記に配列のループ処理の最終指標として UBound Array.Length を使いましたが、 指標に関係なく配列の要素を順次取得する方法が For Each … Next 処理です。 以下の実際のソースで直感的に分かると思います。
For Each の後ろに配列の要素と同じデータ型の変数を記述し In の後ろに対象となる配列を記述します。
Private Sub btnArray_Click(sender As Object, e As EventArgs) Handles btnArray.Click '配列宣言(初期化) Dim arr() As Integer = {10000, 10000, 1000, 100, 10, 1} '要素宣言 Dim nEnt As Integer '合計計算 Dim nSum As Integer = 0 For Each nEnt In arr nSum += nEnt Next Console.WriteLine("For Each ... Nexを利用した場合の合計処理:{0}", nSum) End Sub
For Each ... Nexを利用した場合の合計処理:21111
尚、要素用の変数を別で宣言しなくても以下の様にしても同様な結果となります。Private Sub btnArray_Click(sender As Object, e As EventArgs) Handles btnArray.Click '配列宣言(初期化) Dim arr() As Integer = {10000, 10000, 1000, 100, 10, 1} '合計計算 Dim nSum As Integer = 0 For Each nEnt As Integer In arr nSum += nEnt Next Console.WriteLine("For Each ... Nexを利用した場合の合計処理:{0}", nSum) End Sub
関連する記事
⇒配列の使い方の注意点について(コピー, Clone)
⇒配列の範囲指定によるコピー(Array.Copy, Skip, Take)
-
あるデータ処理において文字列を Shift-JIS と考えて切出す必要があり、 文字列を Shift-JIS と見立てて切出す関数を作ってみましたので紹介します。
VB.NET において通常、文字列は Unicode(UTF-8) で処理されています。 ほとんどの場合は文字コードのことを特に考えなくても問題は無いのですが、まれにシリアル通信等で外部とのやり取りを行う場合に 文字列のコードが Shift-JIS を使っていたりします。こういった場合に今回の関数群は使えると思います。
では、最初に文字列の中から文字列のバイト位置(Shift-JISと考えて)と切出すバイト数を指定し、処理する関数である MidB(以前の VB では Mid$ とされていたもの)を記します。
開始位置、切出しバイト数の指定値により、全角文字の泣き別れが発生しますので、関数の処理手順は以下の様に行います。- 開始位置が「1」未満の場合は空文字を返す。
- 文字列をShift-JISエンコードでバイト配列に分解し、開始位置が配列の長さより大きい場合は空文字を返す。
- 先頭から見て指定位置の直前までを切出し最後の文字が漢字の左半分の場合、開始位置と切出しバイト数を調整。
- 調整された開始位置とバイト数で文字列を切出し最後の文字が漢字の左半分の場合、切出しバイト数を調整。
- 調整された開始位置とバイト数で文字列を生成する。
MidB 関数
'''
''' 開始位置、バイト指定による文字列切出・Mid関数 ''' ''' <param name="astrSrc">対象文字列</param> ''' <param name="aintStart">開始位置(先頭1から)</param> ''' <param name="aintLen">切出しバイト数</param> '''切出し文字列 Private Function MidB(ByVal astrSrc As String, ByVal aintStart As Integer, ByVal aintLen As Integer) As String Try '開始を「0」からの指標 Dim intStart As Integer = aintStart - 1 '開始が1より小さい場合は空文字を返す If intStart < 0 Then Return "" End If 'Shift-JISでエンコーディングしてバイト配列に分解 Dim objEnc As System.Text.Encoding = System.Text.Encoding.GetEncoding("Shift_JIS") Dim arrBytes As Byte() = objEnc.GetBytes(astrSrc) '開始がバイト配列より大きい場合は空文字を返す If intStart > arrBytes.Length - 1 Then Return "" End If '取り出しバイト数 Dim intLen As Integer = aintLen If (intStart + aintLen) > (arrBytes.Length - 1) Then intLen = arrBytes.Length - intStart End If '先頭から見て指定位置の直前までを切り出す Dim strTemp As String = objEnc.GetString(arrBytes, 0, intStart) If strTemp.EndsWith(ControlChars.NullChar) Or strTemp.EndsWith("・") Then '最後の文字が漢字の左半分の場合、切出の先頭は泣き別れになる intStart += 1 '開始位置を右へ intLen -= 1 '切出バイト数を1個少なくする End If '修正位置とバイト数で切り出す strTemp = objEnc.GetString(arrBytes, intStart, intLen) If strTemp.EndsWith(ControlChars.NullChar) Or strTemp.EndsWith("・") Then '最後の文字が漢字の左半分の場合、泣き別れになる intLen -= 1 '切出バイト数をさらに1個少なくする End If '文字列を返す Return objEnc.GetString(arrBytes, intStart, intLen) Catch ex As Exception Return "" End Try End Function以前の VB の Mid$ では第3引数が無く、開始位置から右側を全て返す関数がありましたので、 同じ関数名で引数の数が異なる関数を以下に記します。
MidB 関数(開始位置以降の切出し)
'''
''' 開始位置以降の文字列切出・Mid関数 ''' ''' <param name="astrSrc">対象文字列</param> ''' <param name="aintStart">開始位置(先頭1から)</param> '''切出し文字列 Private Function MidB(ByVal astrSrc As String, ByVal aintStart As Integer) As String Try '開始を「0」からの指標 Dim intStart As Integer = aintStart - 1 '開始が1より小さい場合は空文字を返す If intStart < 0 Then Return "" End If 'Shift-JISでエンコーディングしてバイト配列に分解 Dim objEnc As System.Text.Encoding = System.Text.Encoding.GetEncoding("Shift_JIS") Dim arrBytes As Byte() = objEnc.GetBytes(astrSrc) '開始がバイト配列より大きい場合は空文字を返す If intStart > arrBytes.Length - 1 Then Return "" End If '実際の処理は上記の関数をコール Return MidB(astrSrc, aintStart, arrBytes.Length - aintStart + 1) Catch ex As Exception Return "" End Try End Functionさらに、文字列の左側から指定バイト数を切り出す LeftB 関数、文字列の右側から指定バイト数を切り出す RightB 関数を記します。 この2個の関数は最初の MidB 関数を利用しています。
LeftB 関数 , RightB 関数
'''
''' バイト数指定による文字列の左側切出し・Left関数 ''' ''' <param name="astrSrc">対象文字列</param> ''' <param name="aintLen">左側切出しバイト数</param> '''切出し文字列 Private Function LeftB(ByVal astrSrc As String, ByVal aintLen As Integer) As String '先頭から指定バイト数を切り出す Return MidB(astrSrc, 1, aintLen) End Function '''''' バイト数指定による文字列の右側切出し・Right関数 ''' ''' <param name="astrSrc">対象文字列</param> ''' <param name="aintLen">右側切出しバイト数</param> '''切出し文字列 Private Function RightB(ByVal astrSrc As String, ByVal aintLen As Integer) As String 'Shift-JISでエンコーディングしてバイト配列に分解 Dim objEnc As System.Text.Encoding = System.Text.Encoding.GetEncoding("Shift_JIS") Dim arrBytes As Byte() = objEnc.GetBytes(astrSrc) 'バイト数がバイト配列の個数より多い場合は、対象文字列そのまま返す If aintLen >= arrBytes.Length Then Return astrSrc End If '開始位置 Dim intStart As Integer = arrBytes.Length - aintLen + 1 '先頭から指定バイト数を切り出す Return MidB(astrSrc, intStart, aintLen) End Functionこれらの関数を実行する例を以下に記します。 これはフォームにボタンを設置し、クリックイベント時に、各関数の処理を行い結果をデバッグ用の出力ウインドウに表示しています。
Private Sub btnStringCut_Click(sender As Object, e As EventArgs) Handles btnStringCut.Click '表示 Dim strSrc As String = "あいうえお" Dim i As Integer '引数が3個ある MidB 関数 Dim nCut As Integer = 6 For i = 0 To 10 Dim strDes As String = MidB(strSrc, i, nCut) Console.WriteLine("MidB(""{0}"", {1}, {2}) : /{3}/", strSrc, i, nCut, strDes) Next '引数が2個の MidB 関数 For i = 0 To 10 Dim strDes As String = MidB(strSrc, i) Console.WriteLine("MidB(""{0}"", {1}) : /{2}/", strSrc, i, strDes) Next 'LeftB 関数 For i = 0 To 10 Dim strDes As String = LeftB(strSrc, i) Console.WriteLine("LeftB(""{0}"", {1}) : /{2}/", strSrc, i, strDes) Next 'RightB 関数 For i = 1 To 11 Dim strDes As String = RightB(strSrc, i) Console.WriteLine("RightB(""{0}"", {1}) : /{2}/", strSrc, i, strDes) Next End Sub
結果の表示は以下の通りです。
MidB("あいうえお", 0, 6) : // MidB("あいうえお", 1, 6) : /あいう/ MidB("あいうえお", 2, 6) : /いう/ MidB("あいうえお", 3, 6) : /いうえ/ MidB("あいうえお", 4, 6) : /うえ/ MidB("あいうえお", 5, 6) : /うえお/ MidB("あいうえお", 6, 6) : /えお/ MidB("あいうえお", 7, 6) : /えお/ MidB("あいうえお", 8, 6) : /お/ MidB("あいうえお", 9, 6) : /お/ MidB("あいうえお", 10, 6) : // MidB("あいうえお", 0) : // MidB("あいうえお", 1) : /あいうえお/ MidB("あいうえお", 2) : /いうえお/ MidB("あいうえお", 3) : /いうえお/ MidB("あいうえお", 4) : /うえお/ MidB("あいうえお", 5) : /うえお/ MidB("あいうえお", 6) : /えお/ MidB("あいうえお", 7) : /えお/ MidB("あいうえお", 8) : /お/ MidB("あいうえお", 9) : /お/ MidB("あいうえお", 10) : // LeftB("あいうえお", 0) : // LeftB("あいうえお", 1) : // LeftB("あいうえお", 2) : /あ/ LeftB("あいうえお", 3) : /あ/ LeftB("あいうえお", 4) : /あい/ LeftB("あいうえお", 5) : /あい/ LeftB("あいうえお", 6) : /あいう/ LeftB("あいうえお", 7) : /あいう/ LeftB("あいうえお", 8) : /あいうえ/ LeftB("あいうえお", 9) : /あいうえ/ LeftB("あいうえお", 10) : /あいうえお/ RightB("あいうえお", 1) : // RightB("あいうえお", 2) : /お/ RightB("あいうえお", 3) : /お/ RightB("あいうえお", 4) : /えお/ RightB("あいうえお", 5) : /えお/ RightB("あいうえお", 6) : /うえお/ RightB("あいうえお", 7) : /うえお/ RightB("あいうえお", 8) : /いうえお/ RightB("あいうえお", 9) : /いうえお/ RightB("あいうえお", 10) : /あいうえお/ RightB("あいうえお", 11) : /あいうえお/
関連する記事
⇒文字列の連結をStringBuilderで高速に行う(StringBuilder)
⇒SerialPortコントロールの使い方その3(外部装置からの垂れ流しデータ受信)
-
文字列の分解は、文字列オブジェクトのメソッド Split を用いて行います。 一般的な例として日付を分解する方法を見てみます。
'文字列の分解の例(年月日を例とする) Dim strTest As String = "2019/08/01" '"/"(スラッシュ)で分解する Dim arr As String() = strTest.Split("/") '3個の配列で「arr」には入る For i As Integer = 0 To UBound(arr) Console.WriteLine("arr({0}) : {1}", i, arr(i)) Next
動作結果として、デバッグ時の出力ウインドウには以下の様に表示されます。 日付文字列が「/」(スラッシュ)で分解されることが分かります。 但し、注意する点は、配列要素には指標が「0」から順に格納されることです。
上の例では、文字列配列「arr」が何個返されるか分からないので、 UBound 関数を使って最後の指標の値を指定しています。arr(0) : 2019 arr(1) : 08 arr(2) : 01
さらに、以下の例を見て下さい。
'文字列の分解の例(年月日を例とする:スラッシュは無い) strTest = "20190801" '"/"(スラッシュ)で分解する arr = strTest.Split("/") '1個の配列で「arr」には入るはず For i As Integer = 0 To UBound(arr) Console.WriteLine("arr({0}) : {1}", i, arr(i)) Next
実行結果は以下の通りです。
arr(0) : 20190801
分解する文字列にはスラッシュは無いので、元の文字列が要素が1個の配列の先頭に返されます。
これは少し、問題が有りまして、例えば分解すべき文字列がテキストボックスか何かで入力する場合、 スラッシュで分解された配列が3個だと固定で考えて組んでしまうと、エラーが発生するはずです。 それが、以下の様な例です。'文字列を「TextBox1」から取るとする Dim strTest As String = Me.TextBox1.Text '"/"(スラッシュ)で分解する Dim arr As String() = strTest.Split("/") '3個の配列で「arr」には入る Dim strYMD As String = arr(0) + arr(1) + arr(2) Console.WriteLine("日付 : {0}", strYMD)
「TextBox1」に「2019/08/01」等のスラッシュがあるデータが入力されればいいのですが、 「20190801」と入力すると、日付の連結のところでエラーが発生します。
このエラーを回避するには、分解された文字列配列の個数をチェックする方法があります。
または、テキストボックスでは無く必ず日付文字列が返されるであろう日付型コントロールを使うのも一つです。
Split には同じ名前で別の引数を持つメソッドがあります。 以下の例は、分解文字を配列で与えるメソッドです。 「/」(スラッシュ)「 」(スペース)「:」(コロン)を文字配列で与えることで、年月日時刻を一括で分解することが出来ます。'文字列の分解の例(年月日を例とする) Dim strTest As String = "2019/08/01 13:15:47" '"/"(スラッシュ)で分解する Dim arrSplit As Char() = {"/"c, " "c, ":"c} Dim arr As String() = strTest.Split(arrSplit) '3個の配列で「arr」には入る For i As Integer = 0 To UBound(arr) Console.WriteLine("arr({0}) : {1}", i, arr(i)) Next
実行結果は以下の通りです。
arr(0) : 2019 arr(1) : 08 arr(2) : 01 arr(3) : 13 arr(4) : 15 arr(5) : 47
関連する記事
⇒文字列の連結をStringBuilderで高速に行う(StringBuilder)
⇒文字列定数(改行、タブ、バックスペース等)について
⇒文字列変換関数(StrConv)で変換が途中で切れる
⇒文字列変換関数(StrConv)の使い方
-
EXCELファイルの拡張子が xlsx のファイルですが、多分 EXCEL2007以降のバージョンでEXCEL2010,2013,2015などで出力される形式があります。 このファイルですが、中身はXMLファイルの集まりが圧縮されているそうなのです。 お恥ずかしい限りですが、つい最近まで知らなかったのですが、EXCELファイルをZIPファイルにリネームし圧縮を解凍すれば中身が見られます。
そこで、どうなっているのかを調べてみたので順に見ていきます。
■先ずはテスト用のEXCELファイルを作成します。簡単な内容のデータを作成します。
■フォルダの中に test.xlsx ファイルが作成されました。
■test.xlsx ファイルのコピーを作成します。
■test.xlsx のコピーファイルのファイル名を test.zip に変更します。
■test.zip ファイルを展開してやります。
以下は test.zip ファイルの上で右クリックをしたところですが、その中の「すべて展開(T)...」を選択します。 (私のPCでは各種ツールが入っていますので、変なメニューも表示されていますが)
■展開結果は以下の様になります。
展開された中のXMLファイルを見てみます。 XMLファイルの中身を見たい場合にはXML表示ソフトを使いますが、以下のフリーソフト「XMLEDITOR.NET(xml.exe)」が使いやすいと思います。 このソフトを使ってXMLファイルをのぞいてみます。
⇒XMLEDITOR .NETについて
■\test\xl\worksheets フォルダの中の sheet1.xml ファイルを「XMLEDITOR.NET(xml.exe)」で見てみます。
以上からエクセルのファイルは各種のXMLファイルで構成されていることが分かります。 尚、今回なぜエクセルの中身を解析する必要が在ったかと言いますと、PHPでのエクセル出力で、エクセルのグラフの表現がどうなっているかを調査するためでした。
VB.NETからエクセルを使う場合はエクセルのオブジェクトからそのメソッド等を利用して操作ができるので、中身までは意識しませんが、 PHPからの処理ではPHPExcleライブラリだけでは難しい部分もありますので、参考になればと思います。関連する記事
⇒Excel ファイルの操作について(Microsoft.Office.Interop.Excel COM参照)