配列データについて

タグの編集
投稿者 カラス  (社会人) 投稿日時 2014/7/14 23:38:14
お世話になっております。
Visual Basic 2010を使用しています。

csvから値を読み込み、配列を使ってデータを取り出したいのですが、中々うまくいきません。
「インデックスが配列の境界外です。」と出てしまいます。

タブ句切りをした後に、さらに「\\」で区切って、さらに「\\」で区切った値を分けて使用したいです。

下記の条件も加えてやっています。
・Dataが配列1~5まであるとして、値がなければスルー
・値があればaに値を追加していき、その値を最後に別のcsvに書き込む
・後ろの値から書き込む

test.csv
---------------------------------------------------------
"名前""説明"
"User1" "テスト1"
"User2\\パスワード1" "テスト2"
"User3\\パスワード2\\コメント" "テスト3"
"User4\\パスワード3" "テスト4"
....
---------------------------------------------------------

        Dim Reader As New System.IO.StreamReader("C:\test\test.txt")
        Dim Writer As New StreamWriter("C:\test\test2.txt")

        Dim a As String = Nothing
        Dim Items()
        Dim Line As String = Reader.ReadLine 'csv1行読み込み

        Line = Reader.ReadLine 'ファイルの1行目を飛ばす

        Do Until IsNothing(Line) 'データがなくなるまで読み込む

            Items = Line.Split(vbTab) 'タブ句切り
            Dim Data() = Split(Items(0), "\\") '名前の方の値を\\句切り

            For i As Integer = 4 To 0 Step -1
                If Data(i).Length <> Nothing Then
                    a = a + "データ=" + Data(i)
                Else
                End If
            Next

            Writer.WriteLine("ユーザ情報=" & a & ",説明=" & Items(1) & "")

            Line = Reader.ReadLine '次の行を読み込む
        Loop

        Reader.Close()
        Writer.Close()

助言よろしくお願いします。
投稿者 shu  (社会人) 投稿日時 2014/7/15 07:59:20
少し前に解決した投稿に対し別の問題が発生したのでしょうか?


提示した例外が関係しそうな箇所は
> Dim Data() = Split(Items(0), "\\") '名前の方の値を\\句切り


> If Data(i).Length <> Nothing Then


> a = a + "データ=" + Data(i)


> Writer.WriteLine("ユーザ情報=" & a & ",説明=" & Items(1) & "")


この辺ですね。
配列の大きさを
チェックして処理する必要があります。
投稿者 カラス  (社会人) 投稿日時 2014/7/15 11:00:55
返信ありがとうございます。

前回の区切りはなんとか出来ました。

指摘していただいた通り、
> If Data(i).Length <> Nothing Then
ここで表示されます。

Redim Data(4)を追加して大きさを指定してみたのですが、
うまくいきません。
投稿者 鳩山田小十郎  (社会人) 投稿日時 2014/7/15 12:01:18
>Redim Data(4)を追加して大きさを指定してみたのですが、

これだと出来るのはData(0)~(3)の4個じゃないの?
そこでData(4)を要求されたらそりゃインデックスはみ出てるって言うよ
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2014/7/15 13:11:38
>> ・Dataが配列1~5まであるとして、値がなければスルー
分割された Data が 5 個以上になるためには、
その行に、「\\」が 4 個以上存在する必要がありますね。


>> Dim Data() = Split(Items(0), "\\") '名前の方の値を\\句切り
>> For i As Integer = 4 To 0 Step -1
>>    If Data(i).Length <> Nothing Then
i の範囲を 4 ~ 0 固定としていますが、その場合、Items(0) 内に
「\\」が4個以上あることを保証しなければなりません。

仮に、「\\」が 4 個未満だった場合、Data(4) は存在しないため、
『インデックスが配列の境界外です』のエラーになってしまうでしょう。

もし、「\\」の数が未定義なのであれば、配列の要素数に応じた数を指定するために、
 For i As Integer = UBound(Data) To LBound(Data) Step -1
のように、UBound 関数や GetUpperBound メソッド、あるいは Length プロパティなどを
併用すると良いでしょう。もしくは、For Each を使うというのも手です。


なお、配列数が 5 個に満たない場合には、そもそも列挙したく ないのであれば、
For での列挙前に、「If Data.Length < 5 Then」などの条件で除外しておくと良いでしょう。
5 個以上あることが確実なら、元の「For i As Integer = 4 To 0 Step -1」のままでも問題ありません。


>> If Data(i).Length <> Nothing Then
文字列の長さを調べるのであれば、Nothing と比較するのではなく、
 If Data(i).Length <> 0 Then
のように、 0 と比較する方が素直だと思いますよ。

仮に、Data(i) そのものが Nothing では無いことを判定する意図だとすれば、
 If Data(i) IsNot Nothing Then
もしくは
 If Not Data(i) Is Nothing Then
と書く必要があります。

あるいは、
 If String.IsNullOrEmpty( Data(i) ) Then
といった書き方もあります。これは、Nothing か空文字かを判定します。

>  a = a + "データ=" + Data(i)
文字列連結時には、「+ 演算子」ではなく「& 演算子」を使うほうが望ましいです。VB では。


>>Redim Data(4)を追加して大きさを指定してみたのですが、
今回、ReDim は不要というか無意味です。
配列サイズの確保は Split 関数側で行われますので、事前に ReDim したとしても、
最終的には「\\」の で分割された総数分の配列を示すことになります。

Dim Data() As String    'この時点では、Data は Nothing 
ReDim Data(100)    'この時点では、Data(0)~Data(100) が確保される 
Data = Split(Items(0), "\\")    'Data(0)~Data(「\\」の出現数 + 1) な配列で上書きされる 





> これだと出来るのはData(0)~(3)の4個じゃないの?
違います。VB の配列宣言は、『配列の添字の最大値』を指定しますので、
『ReDim Data(0 To 4)』や『ReDim Data(4)』は、Data(0)~Data(4)が確保されます。
投稿者 鳩山田小十郎   (社会人) 投稿日時 2014/7/15 13:19:49
あー
VBは最大値の指定だったんですねぇ。
投稿者 HiDE-Ada  (社会人) 投稿日時 2014/7/15 18:13:08
単に
    a=""     '' 宣言時にNothingを代入しているだけなので、毎回初期化
    For i As Integer = Data.Length-1 To 0 Step -1
        a &= "データ=" & Data(i)   '' &=って使えたかな?+=ならOK?
    Next
でよいのでは?
ただ、結果をみるとわかりますが
 ユーザ情報=データ=コメントデータ=パスワード2データ=User3,説明=テスト3
のようになっちゃいますね。
投稿者 カラス  (社会人) 投稿日時 2014/7/15 20:27:32
皆様ありがとうございます。

for文の開始を
For i As Integer = UBound(Data) To LBound(Data) Step -1
に変え、If文の開始を
If Data(i).Length <> 0 Thenに変え、さらに&演算子を使用することでうまく値を取ることができました。

HiDE-Adaさんのおっしゃるとおり、aを初期化しないといけなかったのでその処理も入れました。