VB2010を使っております。
2次元配列で困っております。
たとえば
Dim strArray()() As String
と
Dim strArray(,) As String
の違いはあるのでしょうか?
実は、某サイトから
Private Function Test(ByVal fileName As String)
' StreamReader の新しいインスタンスを生成する
Dim cReader As New System.IO.StreamReader(fileName, System.Text.Encoding.Default)
Dim textLines As New List(Of String())
' 読み込んだ結果をすべて格納するための変数を宣言する
Dim stResult As String = String.Empty
' 読み込みできる文字がなくなるまで繰り返す
While (cReader.Peek() >= 0)
' ファイルを 1 行ずつ読み込む
Dim stBuffer As String = cReader.ReadLine()
' 読み込んだものを追加で格納する
Dim cols() As String
cols = Split(stBuffer, ",")
textLines.Add(cols)
End While
cReader.Close()
Dim strArr()() As String
strArr = textLines.ToArray
Return strArr
End Function
というソースを引っ張ってきたのですが、
Dim strArray()() As String
には格納できて、
Dim strArray(,) As String
には格納できません。
何か勘違いしているところがあるのでしょうか。
> Dim strArray()() As String
> と
> Dim strArray(,) As String
> の違いはあるのでしょうか?
違うものであることは、すでに気づいておられるのですよね。実際、
> には格納できません。
という状況になっているわけですし。
まず、後者は二次元配列ですが、前者はそうではありません。
専門用語では「ジャグ配列」と呼ばれる種類のものです。
後者は二次元配列であり、その個々の要素は「String」という型ですが、
前者は一次元配列であり、その個々の要素は「String の一次元配列」です。
配列の配列、それがジャグ配列(jagged arrays)です。
http://msdn.microsoft.com/ja-jp/library/hkhhsz9t.aspx
たとえば、こんなコードがあるとします。a はジャグ配列、b は二次元配列です。
Dim a()() As Integer = {
New Integer() {1, 2, 3},
New Integer() {4, 5, 6, 7},
New Integer() {8, 9}
}
MsgBox(TypeName(a)) 'Integer()()
MsgBox(TypeName(a(0))) 'Integer()
MsgBox(TypeName(a(1)(2))) 'Integer
Dim b(,) As Integer = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}
MsgBox(TypeName(b)) 'Integer(,)
MsgBox(TypeName(b(0, 0))) 'Integer
a の方は、a(0) と a(1) で要素数が異なっていますよね。
一方 b は、b は 3x3 の二次元配列です。仮に b を
Dim b(,) As Integer = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9, 10}
}
とした場合、四角形な二次元配列にならず、エラーとなります。
3x4 となるよう
Dim b(,) As Integer = {
{1, 2, 3, 0 },
{4, 5, 6, 0},
{7, 8, 9, 10}
}
のようにする分には OK ですけれどね。これが両者の違いです。
そしてジャグ配列は、単に「配列の配列」というだけですので、
下記のようなパターンもアリです。
Dim a()() As Integer '1次元配列を格納した1次元配列
Dim b(,)() As Integer '1次元配列を格納した2次元配列
Dim c()(,) As Integer '2次元配列を格納した1次元配列
Dim d(,)(,) As Integer '2次元配列を格納した2次元配列
そうなんですね。
Dim strArray(,) As String
に格納できる、Function関数など何かございますでしょうか。CSVファイルから読み込むものなんですけど。
ジャグ配列のままだと、何か都合が悪いのでしょうか?
二次元配列だと、行数や列数を変化させる場合に手間がかかりそうですし、
特に理由がなければ、今のコードのままでも問題ないように思います。
格納済みの要素にアクセスするなら、
「boxDimArray(rowIndex, colIndex)」が
「jaggedArray(rowIndex)(colIndex)」
という表記に変わるだけですし、行/列番号を指定して扱う分には大差はないかと。
> Function関数など何かございますでしょうか。
COM Interop を伴うのでお奨めはしませんが(個人的には非推奨)、一応、
ADODB.Recordset の GetRows メソッドが二次元配列を返すようになっています。
> CSVファイルから読み込むものなんですけど。
二次元配列に格納するには、csvの行数/列数をあらかじめ調べねばならないため、
いったん DataTable やジャグ配列に蓄えて、あとで変換する実装が多いかと思います。
(列数は事前に分かることもあるが、行数は可変にせざるを得ない)
列数があらかじめ分かっている場合は、行と列の順序を Dim box(列数, 行数) As String の
順にしておき、行数に応じて ReDim Preserve ステートメントで調整する実装もありますが、
それなら、ジャグ配列から変換した方が楽でしょうね。
> Dim strArray(,) As String
どうしても二次元配列が必要ということであれば…。
既に完成しているジャグ配列から、インデックスの最大値(あるいは要素数)を調べる方法は分かりますか?
それができれば、そこから二次元配列に並び替えるのは容易ですよね。
インデックスの最大値は、UBound 関数または GetUpperBound メソッドで調べられます。
要素数なら、Length プロパティです。
'行番号/列番号の最大値を調べる。
'CSV の列数が事前に分かっているなら、colMaxIndex の方は固定値でも OK。
Dim rowMaxIndex As Integer = jaggedArray.GetUpperBound(0)
Dim colMaxIndex As Integer = Aggregate row In jaggedArray Into Max(row.GetUpperBound(0))
'二次元配列として、必要な行数/列数を確保する
Dim boxDimArray(rowMaxIndex, colMaxIndex) As String
'ジャグ配列から二次元配列に転記する
For rowIndex As Integer = 0 To rowMaxIndex
For colIndex As Integer = 0 To colMaxIndex
boxDimArray(rowIndex, colIndex) = jaggedArray(rowIndex)(colIndex)
Next
Next
'=========================================================
あるいは、ジャグ配列にしてから変換するのではなく、
Split や TextFieldParser で切り出すたびに、
二次元配列に格納していっても良いかと。
'csvファイルを、行ごとに区切った1次元配列にする
Dim rows() As String = System.IO.File.ReadAllLines(
"C:\temp\test.csv",
System.Text.Encoding.GetEncoding("Shift_JIS"))
'空行を読み捨てる。
rows = rows.Where(Function(row) Not String.IsNullOrWhiteSpace(row)).ToArray()
'行数を調べる
Dim rowMaxIndex As Integer = rows.GetUpperBound(0)
'先頭行の「,」の数から、列数を調べる
Dim cols() As String = rows.First().Split(",")
Dim colMaxIndex As Integer = cols.GetUpperBound(0)
'行数/列数分の二次元配列を確保する
Dim strArray(rowMaxIndex, colMaxIndex) As String
'確保した二次元配列に、csvデータを格納していく
For rowIndex As Integer = 0 To rowMaxIndex
cols = rows(rowIndex).Split(",")
For colIndex As Integer = 0 To colMaxIndex
strArray(rowIndex, colIndex) = cols(colIndex)
Next
Next
魔界の仮面弁士さま
2つのケース、まさにどちらも使いたかったケースです。
'ジャグ配列から二次元配列に転記する
For rowIndex As Integer = 0 To rowMaxIndex
For colIndex As Integer = 0 To colMaxIndex
boxDimArray(rowIndex, colIndex) = jaggedArray(rowIndex)(colIndex)
Next
Next
'確保した二次元配列に、csvデータを格納していく
For rowIndex As Integer = 0 To rowMaxIndex
cols = rows(rowIndex).Split(",")
For colIndex As Integer = 0 To colMaxIndex
strArray(rowIndex, colIndex) = cols(colIndex)
Next
上記のループして配列に格納、まさにこのイメージがほしかったのです。なんかいいすぎかもしれませんが、体から悶々としたものがスッキリしました。
ツイート | ![]() |