ファイルを読み込んで配列に格納する方法

タグの編集
投稿者 はち  (社会人) 投稿日時 2021/10/15 13:38:51
4列のテキストデータ(スペース区切り)を読み込んで,一列ごとに配列に格納する方法を教えていただけませんでしょうか.
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/10/15 20:00:35
> 一列ごとに配列に格納する方法

えぇと…2行4列構成の
111 222 333 444
555 666 777 888
というテキストファイルがあったと仮定して:

data(0, 0) = "111"
data(1, 0) = "555"
data(0, 3) = "444"
data(1, 3) = "888"
のような「2 次元配列」になればよいのでしょうか。

それとも
col1(0) = "111"
col1(1) = "555"
col4(0) = "444"
col4(1) = "888"
のように、列それぞれで別々の「1 次元配列」に入れたいのでしょうか。

あるいは、1列目~4列目の情報を行ごとの配列に入れて
data(0)(0) = "111"
data(1)(3) = "888"
のように、「配列の配列」として管理したいのでしょうか。


また、お使いの開発言語とバージョンは何でしょうか?


ファイルの文字コードなど、仕様として明らかになっていない点が多々ありますが、
ひとまず、ジャグ配列として取得する場合の例を挙げてみます。

VB2010 以降の場合
Dim fileName As String = "C:\test\example.txt"
Dim data()() As String = System.IO.File.ReadLines(fileName, _
    System.Text.Encoding.GetEncoding("Shift_JIS")).Select(AddressOf Split).ToArray()



VBA の場合
Dim fileName As String
fileName = "C:\test\example.txt"
Dim data() As Variant    'この変数に格納する 

Dim fno As Integer
fno = FreeFile(0)
Open fileName For Binary Access Read As #fno
Dim lines() As String
lines = Split(StrConv(InputB(LOF(fno), fno), vbUnicode), vbCrLf)
Close #fno: fno = 0
ReDim data(UBound(lines)) As Variant
Dim r As Long
For r = LBound(data) To UBound(data)
    data(r) = Split(lines(r), " ")
Next
Erase lines



VBScript の場合
Option Explicit
Dim fileName
fileName = "C:\test\example.txt"
Dim data  'この変数に格納する 

Const adTypeText = 2
Const adReadLine = -2
Dim d, stm
Set d = CreateObject("Scripting.Dictionary")
Set stm = CreateObject("ADODB.Stream")
stm.Open
stm.Type = adTypeText
stm.Charset = "Shift_JIS"
stm.LoadFromFile fileName
Do Until stm.EOS
    d.Add CStr(d.Count), Split(stm.ReadText(adReadLine), " ")
Loop
stm.Close
Set stm = Nothing
data = d.Items()
Set d = Nothing
投稿者 はち  (社会人) 投稿日時 2021/10/16 09:17:45
早速のご返答ありがとうございます.感謝いたします。
配列の方法ですが,以下のようになります.
12 36  45  5
 7 69  21 30  
A(1)=12,B(1)=36,C(1)=45,D(1)=5
A(2)=7,B(2)=69,C(2)=,D(2)=30
使用している言語はVB2019です.VB6で作ったプログラムをVB2019に書き換えようとしているのですがファイルの読み書きで苦戦しております.
よろしくお願いいたします。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/10/16 11:56:21
> 12 36  45  5
>  7 69  21 30

むむ…!?

数字はすべて半角ですが、区切りとなる空白部には
半角スペースの" " は一切用いられておらず、すべて
全角スペースの" " が使われていますね。
しかも列によって、スペースの数が異なるようで。

固定長のデータというわけでもないですし、かといって
スペースの数が数字の桁数に依存しているわけでも無さそう…?
かなり変則的ですね。

視認しやすいよう、全角スペースを別の文字に置き換えたものを載せておきます。
12🟩36🟩🟩45🟩🟩5
🟩7🟩69🟩🟩21🟩30



質問文からは正確な仕様が読み切れなかったので、ここでは仮に下記の仕様として話を進めますね。

① 情報源となる「4列のテキストデータ」は、Shift_JIS で書かれたテキストファイルから得るものとする。
② 1個以上の連続した空白を区切り文字とみなす。
③ 4列構成を前提とし、空要素な列は無いものとする。
④ 各列の中身は半角数字のみで構成され、"01" などは "1" と同義。全角数字や小数は無し。
⑤ 列を区切る空白として、全角スペース" "・半角スペース" "・水平タブ" "の 3 種を許容する。
⑥ 行頭(あるいは行末)には、0個以上の空白を含めても良い。
⑦ 行と行の間には、CrLf 改行が含まれる。(行末改行ではなく行間改行なので、最終行には改行が無い)



> C(1)=45
C(1)="45" ではなく C(1)=45 となっていることから、
String の配列ではなく、数値の配列が必要なのですね。
今回はひとまず Integer ということにしておきます。

> A(1)=12
> B(1)=36
全角の変数と半角の変数が混在しているのが気になりますが、あえてそうしているのでしょうか?
話がややこしくなるため、ここでは半角1文字の A/B/C/D に統一させていただきますね。


ところで、最初のインデックスが (0) ではなく (1) なのは何故でしょうか。
(もしかして、先頭に列名を表す行があって、それをスキップしているとか?)

御存知とは思いますが、.NET では Option Base 1 な配列を選択することができません。

.NET そのものは、A(1 To 2) や A(-3 To -1) といった配列もサポートしているのですが、
VB.NET の方は言語仕様として、0 ベースでない限りはプリミティブな配列として扱われません。
そのため今回は、A(0) = 12、A(1) = 7 という結果になる前提に書き換えさせてもらいます。

なお、VB6 の経験をお持ちならわかると思いますが、配列というものは
事前に要素数の定義が必要です。行数が可変なファイルの読み取りに使うのは不便です。

VB6 でも VB.NET でも、一応は「ReDim Preserve」ステートメントを用いることで、
元データを維持したまま要素数を事後変更できますが、このステートメントは、
 ①新しい配列を別メモリに確保
 ②そこに既存のデータを複写
 ③古い方のメモリを破棄する
という 3 ステップを内部的に行うため、要素数が増えるほど、
ReDim Preserve のパフォーマンスは明らかに落ちていきます。

そのためこういったケースでは、配列ではなく List(Of ) クラスを用いた方が良いでしょう。
List(Of ) ならば、事前に要素数を決める必要がありませんし、追加・挿入・削除も容易です。

また、List(Of ) と一次元配列は、.ToArray() および .ToList() という拡張メソッドを使うことで
相互変換もできるので、既存ソースへの影響もほとんどないはずです。

とはいえ今回は配列がお望みということなので、その前提で組んでみました。
要件定義で不明瞭な点は、こちらで好き勝手に定めてしまっているので、
そちらの事情に合わない部分は、適宜修正してください。

'Imports System.IO 
'Imports System.Text 

'ファイル名と文字コードは適宜修正してください 
Dim fileName As String = "C:\test\example.txt"
Dim enc As Encoding = Encoding.GetEncoding("Shift_JIS")

'列ごとに区切る処理です。 
Dim q = (
    From s In File.ReadLines(fileName, enc)
    Let ary = s.Replace(" ", vbTab).Replace(" ", vbTab).Split(
        New String() {vbTab}, StringSplitOptions.RemoveEmptyEntries)
    Where ary.Length = 4 '←4列以外の行も許容する場合はこのWhereを省きます 
    Select ary).Select(
        Function(Columns, RowIndex) New With {Key RowIndex, Columns}
    ).ToArray()

'件数が分かったので、配列4つを初期化しておき… 
Dim upperIndex As Integer = q.Length - 1
Dim A(upperIndex), B(upperIndex), C(upperIndex), D(upperIndex) As Integer

'整数に変換しながら、文字列配列から整数配列に代入しなおします(変換失敗時は 0 を代入) 
For Each x In q
    Integer.TryParse(x.Columns(0), A(x.RowIndex))
    Integer.TryParse(x.Columns(1), B(x.RowIndex))
    Integer.TryParse(x.Columns(2), C(x.RowIndex))
    Integer.TryParse(x.Columns(3), D(x.RowIndex))
Next


なお今回、途中に 4 列構成ではない行(たとえば6列ある行や2列しか無い行など)が
含まれていた場合、エラーにするのではなく、その行の存在を無視するようにしています。

そのため、配列のインデックスは 0 から始まる番号としてはいますが、
このインデックスは、元のテキストの行番号と必ずしも一致するとは限らない点にご注意を。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/10/16 19:16:15
> なお今回、途中に 4 列構成ではない行(たとえば6列ある行や2列しか無い行など)が
> 含まれていた場合、エラーにするのではなく、その行の存在を無視するようにしています。
> そのため、配列のインデックスは 0 から始まる番号としてはいますが、
> このインデックスは、元のテキストの行番号と必ずしも一致するとは限らない点にご注意を。

別解。

前回は、4 列構成ではない行があった場合に、その行を読み飛ばす仕様にしたため、
行番号がスキップされるケースがありましたが、今回は行番号がずれないようするために、
「5 列以上なら後続列を無視、3列以下なら不足列の値を 0 とみなす」実装にしてみました。

今回は LINQ を使わずに書いてみます。

'Imports System.IO 
'Imports System.Text 

'ファイル名と文字コードは適宜修正すること 
Dim fileName As String = "C:\test\example.txt"
Dim enc As Encoding = Encoding.GetEncoding("Shift_JIS")

'今回は先に件数を調べるので、ReadLines ではなく ReadAllLines を使う 
Dim lines As String() = File.ReadAllLines(fileName, enc)

'配列の事前準備が必要なところまでは同じ 
Dim upperIndex As Integer = lines.GetUpperBound(0)
Dim A(upperIndex), B(upperIndex), C(upperIndex), D(upperIndex) As Integer

'数値化処理。CInt で数値化しても良いけれど、それだと変換エラー時に止まってしまうので…。 
Dim FillInt = Sub(inText$, ByRef outInt%) Integer.TryParse(inText, outInt)

Dim splitter As Char() = $" {vbTab} ".ToCharArray()
Dim separator = StringSplitOptions.RemoveEmptyEntries
For rowIndex = 0 To upperIndex
    '前回は Replace で vbTab 区切りに揃えたけれど、よく考えたら直接 Split すれば十分だった 
    Dim columns() As String = lines(rowIndex).Split(splitter, separator)

    '別々の変数にセットする必要がなければ、こんな回りくどい処理をしなくて済むのだけれど… 
    If columns.Length >= 1 Then FillInt(columns(0), A(rowIndex))
    If columns.Length >= 2 Then FillInt(columns(1), B(rowIndex))
    If columns.Length >= 3 Then FillInt(columns(2), C(rowIndex))
    If columns.Length >= 4 Then FillInt(columns(3), D(rowIndex))
Next
投稿者 はち  (社会人) 投稿日時 2021/10/18 12:12:28
対応いただきありがとうございます。
当方の情報が不明確で申し訳ありません.下記のようなデータを読み込もうとしております.
下記のようなVB6の命令文でファイルに書き込まれた下記のようなデータを読み込もうとしております.
Print #1, X(DT), Y(DT), U1(DT), -U2(DT)・・・実際は半角文字

30.34137       37.00964      0.766        -0.116 
 15.51356      36.96014      8.699         0.717 
 33.5104        36.91454      8.216         0.461 
-2.653893      36.87263      12.746     -0.426 
 実際のデータの行頭はすべてそろっております.
配列の番号が0ではないのは,私の癖で1から配列をしようしています.VB.NETでは0からはじめるのであればそれに従う必要がありますね.
配列の数ですが,VB6では,想定して適当な大きさの配列を宣言しておりました.VB6で作成したプログラムであれば,以下のようにしておりました.
Dim X(20000000) As Single
Dim Y(20000000) As Single
Dim UD1(20000000) As Single
Dim UD2(20000000) As Single
今回お示しいただいた,プログラムはデータの個数を予めカウントし,それを用いて配列宣言をするのですね.
頂いたプログラムをコピペ―ストして,適当に変えさせていただきました.その結果,いくつかのコマンドに赤の波下線がついており,エラー?なのかと思われます.
ご教示いただければ幸いです。よろしくお願いいたします。

 'ファイル名と文字コードは適宜修正してください 
        Dim fileName As String = Label2.Text
        Dim enc As Encoding = Encoding.GetEncoding("Shift_JIS")・・・Encordingが定義されていません

        '列ごとに区切る処理です。 
        Dim q = (
    From s In File.ReadLines(fileName, enc)・・・・ファイルは定義されていません.
    Let ary = s.Replace(" ", vbTab).Replace(" ", vbTab).Split(
        New String() {vbTab}, StringSplitOptions.RemoveEmptyEntries)
    Where ary.Length = 4 '←4列以外の行も許容する場合はこのWhereを省きます 
    Select ary).Select(
        Function(Columns, RowIndex) New With {Key RowIndex, Columns}
    ).ToArray()

        '件数が分かったので、配列4つを初期化しておき… 
        Dim upperIndex As Integer = q.Length - 1
        Dim X(upperIndex), Y(upperIndex), UD1(upperIndex), UD2(upperIndex) As Single

        '整数に変換しながら、文字列配列から整数配列に代入しなおします(変換失敗時は 0 を代入) 
        For Each X In q
            Single.TryParse(X.Columns(0), A(X.RowIndex))・・・・Columns,RowindexはSngleのメンバーではありません.
            Single.TryParse(X.Columns(1), B(X.RowIndex))
            Single.TryParse(X.Columns(2), C(X.RowIndex))
            Single.TryParse(X.Columns(3), D(X.RowIndex))
        Next
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/10/18 14:20:05
> 当方の情報が不明確で申し訳ありません.
💠プログラミングは一文字違っただけでも異なった結果になりえます。
プログラムコードは全角半角も含めて、一字一句、常に正確な記述を心がけましょう。
エラーメッセージを転記する場合も然り。


> Print #1, X(DT), Y(DT), U1(DT), -U2(DT)・・・実際は半角文字
Print ステートメントでカンマ区切りなので、タブストップ位置まで半角空白が補完されますね。
また、これらの変数は、すべて Single 型の一次元配列とのことなので、
先に提示したコードを Integer → Single に置きかえれば、ほぼそのまま使えると思います。


> その結果,いくつかのコマンドに赤の波下線がついており
🔰言語間の移植の際には、両方の言語の知識が無いと、適切な翻訳ができません。
英語の手紙をフランス語に翻訳する際には、英語とフランス語の両方の知識が求められますよね。

機械的な自動変換に頼って翻訳することもできなくは無いですが、それぞれの
開発言語の特性を理解していないと、回りくどい処理になってしまいがちです。


> Dim enc As Encoding = Encoding.GetEncoding("Shift_JIS")・・・Encordingが定義されていません
『Encording』ではなく
「Encoding」ではありませんか? スペルをもう一度確認してみてください。


> いくつかのコマンドに赤の波下線がついており
スペルが正しい場合、エラーとして表示される波線部にマウスカーソルを重ねると、
修正候補が提示されるので、それをクリックすることで解決できることが多いです。


今回の場合は、「ファイルの先頭にて、名前空間を Imports しているかどうか」が肝です。

先のコードではそれが分かるよう、コメントにて
> 'Imports System.IO 
> 'Imports System.Text
という文を示してあったと思います。
この宣言をファイルの先頭に移動して、コメントを解除してみてください。

もしも名前空間や Imports という言葉が分からない場合は、VB 中学校の 初級講座 [改訂版] の
  第5回 クラスと名前空間
  第7回 クラスを使う仕組み
あたりを参照してみると良いでしょう。
https://www.umayadia.com/VBStandard2/VBStandard2Toc.htm


Imports System.Text として名前空間を指定しておくことで、
Dim enc As System.Text.Encoding という宣言を
Dim enc As Encoding と短く書くことができるようになります。


> From s In File.ReadLines(fileName, enc)・・・・ファイルは定義されていません.
『ファイルは定義されていません.』ではなく
「'File' は宣言されていません。」ですよね?

ファイル → 'File'
    定義 → 宣言
      . → 。


だとすればこれも、 System.IO 名前空間が示されていないことが原因です。
以下のいずれかを行ってみてください。個人的には 2 または 1 をお奨めします。

修正案1) ファイルの先頭にて「Imports System.IO」という宣言文を追加しておく。

修正案2) プロジェクトのプロパティの[参照]タブにて、
 「インポートされた名前空間」の一覧から System.IO の欄に☑を付ける。

修正案3) File クラスの名前空間である System.IO. を、省略せずに毎回書き添える。
 From s In System.IO.File.ReadLines(fileName, enc)


> For Each X In q
>   Single.TryParse(X.Columns(0), A(X.RowIndex))・・・・Columns,RowindexはSngleのメンバーではありません.
変数の型が間違っているからですね。X のデータ型を見直してみてください。

我々ふたりのコードを見比べてみましょう。

'私の書いたコード 
Dim A(upperIndex), B(upperIndex), C(upperIndex), D(upperIndex) As Integer
For Each x In q
  Integer.TryParse(x.Columns(0), A(x.RowIndex))
  Integer.TryParse(x.Columns(1), B(x.RowIndex))

' はちさんの書いたコード 
Dim X(upperIndex), Y(upperIndex), UD1(upperIndex), UD2(upperIndex) As Single
For Each X In q
  Single.TryParse(X.Columns(0), A(X.RowIndex))
  Single.TryParse(X.Columns(1), B(X.RowIndex))



Integer → Single への置き換えは良いと思いますが、
私のコードにあった変数 A, B, C,D の宣言は、一体どうされたのでしょうか。
配列名として A,B,C,D という名前を指定されたのは、はちさん御自身だったはずですよね。

それなのにその宣言を削除すれば、A(x.RowIndex) という指定が出来なくなることは自明です。
一見すると名前を変更したように見えますが、ループ内の変数名は A, B, C, D のままですし…。

列を表す変数を、Integer 配列な B, C から、Single な Y, U1 などへ置き換えるのであれば、
  Integer.TryParse(x.Columns(1), C(x.RowIndex))
という私のコードは
  Single.TryParse(x.Columns(1), U1(x.RowIndex))
になるべきですが、はちさんのコードでは、
  Single.TryParse(X.Columns(2), C(X.RowIndex))
になっていますよね。置き換える場所を間違えています。


それ以上に問題なのは、For Each のループカウンタで使っていた変数 x が、
「Dim X(upperIndex) As Single」と宣言しなおされている点です。

元のコードでの「For Each x In q」の x とは、Columns プロパティや RowIndex プロパティを持つ
『匿名型』の変数だったのに対して、それを勝手に「Dim X(upperIndex) As Single」に変更したため、
『Single 型』の変数に置き換わってしまっています。

Single 型には Columns プロパティや RowIndex プロパティなどありませんよね。


匿名型は良く分からない…というのであれば、
2021/10/16 11:56:21 の投稿コードの代わりに
2021/10/16 19:16:15 の投稿コードを使ってみてください。
(Visual Basic 2015 以降向けに書かれたコードです)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/10/18 14:38:01
> 元のコードでの「For Each x In q」の x とは、Columns プロパティや RowIndex プロパティを持つ
> 『匿名型』の変数だったのに対して、それを勝手に「Dim X(upperIndex) As Single」に変更したため、
> 『Single 型』の変数に置き換わってしまっています。

少し訂正。
『Single 型』の変数ではなく
『Single() 型』の変数に置き換わっています。

Single 型の一次元配列と言い変える事もできます。


> ・・・・Columns,RowindexはSngleのメンバーではありません.
このエラーメッセージも、
Rowindex ではなく RowIndex のはずですし、
Sngle ではなく Single だったはずですね。

'Columns' は 'Single()' のメンバーではありません。
'RowIndex' は 'Single()' のメンバーではありません。




ひとまず、既定の名前空間だけで動くように見直して、
配列変数名も X / Y / UD1/ UD2 とした Single 配列バージョンに書き直しました。

文字列補間( $ で始まる文字列 ) も使わないようにしているので、
VB2008 以降であればコンパイルが通るかと思います。(2008 が手元に無いので試せませんが)


なおこのように、VB のバージョンによって回答が変わる事は多いので、
質問時には開発環境を明示するようにしましょう。(たとえば、今回のコードを
VB.NET 2002 / VB.NET 2003 / VB2005 に対応させる場合は、もう少しだけ手直しが必要です)


Dim fileName As String = Label2.Text  ' = "C:\test\example.txt" 
Dim enc = System.Text.Encoding.GetEncoding("Shift_JIS")

Dim lines As String() = System.IO.File.ReadAllLines(fileName, enc)

Dim upperIndex As Integer = lines.GetUpperBound(0)
Dim X(upperIndex), Y(upperIndex), UD1(upperIndex), UD2(upperIndex) As Single

Dim splitter As Char() = {ControlChars.Tab, " "c, " "c}
Dim separator = StringSplitOptions.RemoveEmptyEntries
For rowIndex = 0 To upperIndex
    Dim columns As String() = lines(rowIndex).Split(splitter, separator)

    Single.TryParse(columns(0), X(rowIndex))
    Single.TryParse(columns(1), Y(rowIndex))
    Single.TryParse(columns(2), UD1(rowIndex))
    Single.TryParse(columns(3), UD2(rowIndex))
Next rowIndex
投稿者 はち  (社会人) 投稿日時 2021/10/18 17:57:56
魔界の仮面弁士様
当方の多数の入力ミス,勝手なプログラム変更の件,心よりお詫び申し上げます。魔界の仮面弁士様に大変不快な思いをさせましたこと重ねてお詫び申し上げます.ご指摘のとおり,プログラミングの1文字違っただけでも異なった結果が出ることになります.このことを深く肝に命じ,以降慎重に返答をさせていただきます.当方の疑問につきまして,こと細かく返答をいだき誠にありがとうございます.正直,知識不足のため,すべては理解できておりません.すこしづつ勉強させていただきます.当方の不手際にも関わらず,変数名を変更したSinglバージョンへ書き直していただき誠にありがとうございました.これをもとに計算を行い,計算結果のファイル出力へ挑みたいと思っております.再度,ご相談させていただけますと大変幸せます.どうぞよろしくお願いいたします。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/10/19 09:52:11
> 魔界の仮面弁士様に大変不快な思いをさせましたこと重ねてお詫び申し上げます.

不快と思っているわけではありませんので大丈夫ですよ。

ただ今回は、無用なやり取りが増えてしまった所があるので、
次回からの質問時には、状況を何も知らない第三者にも伝わる内容になっているかを
投稿前に見直すようにしてみると良いかと思います。


「記載した情報量が十分かどうか」は、なかなか御自身では判断し辛いと思いますが、
「記載した内容が正確かどうか」であれば、文章を見直すことで気付けることが多いので。
(と言いながら、私も今回の回答時に書き間違いをしていたのですけれどね…)

内容が不正確だと、互いの認識に齟齬が生まれてしまいますし、
あるいは最初の質問のように、質問時の前提条件があまりにも少なすぎた場合にも、
適切な回答を付けづらくなり、その分、無駄なやり取りが増えてしまいます。

特に画面に表示されたエラーメッセージについては、正確に記載することをお奨めしておきます。
たとえばコンパイル時のエラーや警告なら、[エラー一覧]ウィンドウから Ctrl+C でコピーできますし、
メッセージボックスに出るエラーも、Ctrl+C でクリップボードにコピーできるようになっているので
それを貼るのが良いと思います。


> 以降慎重に返答をさせていただきます.
> 変数名を変更したSinglバージョンへ書き直していただき誠にありがとうございました.

Singl → Single ですね…。(^^;


それはさておき、最後に一つ蛇足的な情報を。
ここの掲示板では、幾つかの特殊表記がサポートされています。
http://rucio.cloudapp.net/Usage.aspx

半角スペースや全角スペースでの位置合わせに便利な PRE や、
VB のコードを色付けするための CODE といったものが利用できます。参考までに。
投稿者 はち  (社会人) 投稿日時 2021/10/22 15:43:31
魔界の仮面弁士様
寛大な対応いただきありがとうございます。こちらの情報の正確さと量が無駄なやり取りを防止するために大切であること,おっしゃる通りと思います.今後できるだけ努力いたします.と言いながら,いきなりSingleのスペルを間違っており,かなりへこみました.
Ctrl+Cなどの入力の際の便利な方法を教えていただきありがとうございました.早速使ってみます.

おかげさまでファイル読み込みが可能となり,読み込んが数値をもとに計算処理を行い以下のようなファイル出力をしたいと思います.

この次に計算結果の出力として以下のようなVB6で作られたコードをVB2019に移植したいと思います.
①Open "C:\Users\mycomp\Desktop\" + Text1 + ".txt" For Output As #1
②Print #1, BNX + 1, BNZ + 1, DX, DZ
③X1=Xmin
④For XX = 1 To BNX + 1
⑤Z1 = Zmin
⑥For I = 1 To BNZ + 1
⑦Print #1, X1, Z1, MUD1(I), MUD2(I)
⑧Z1 = Z1 + DZ
⑨Next I
⑩X1 = X1 + DX
⑪Next XX
⑫Close #1

①でTextBov1に入力されたファイル名のファイルを開きます.②で整数型で定義された変数BNXに格納された数値に1を足した数と同じく整数型で定義された変数BNYに格納された数値に1を足した値を書き込みます.また同じ行に,実数型で定義されたDXとDZに格納された値を書き込みます.③で実数型で定義された変数X1の初期値を入力されてXminの値とします.④ではForループとして整数型で定義されたXXで1からBNZ+1までの繰り返し処理(同ファイルへの書き込み)を行います.⑤では実数型で定義された変数Z1の初期値を入力されたZminとします.⑥では,Forループとして,整数型で定義されたIを1からBNZ+1まで同ファイルへの書き込み処理来ないます.③と⑤で2重ループとなっています.⑦で同ファイルにX,1,Z1そして別途計算された実数型で定義された配列MUD1(I),MUD2(I)を同じ行に書き込みます.⑧ではZ1に実数型で定義されたdzを足した値を代入します.⑨は⑥に対応するNext Iとなります.⑩はX1に実数型で定義されたdxを足した値を代入します.⑪は④に対応するNext XXです.⑫でファイルを閉じます.

上のプログラムで②と⑦以外はVB2019でも同じになるかと思いますが,②,⑦に対応するVB2019でのコードをご教示いただけませんでしょうか.どうぞよろしくお願いいたします。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/10/22 16:54:44
> TextBov1に入力された
誤:TextBov1
正:TextBox1
の誤記かと思いますが、コードの表記はどちらでもなく、「Text1」になっているという罠…。


> 整数型
バイト型: VB6 では Byte    / VB.NET では Byte
  整数型: VB6 では Integer / VB.NET では Short
長整数型: VB6 では Long    / VB.NET では Integer

> 実数型
単精度浮動小数点型: VB6 では Single  / VB.NET では Single
倍精度浮動小数点型: VB6 では Double  / VB.NET では Double

※他の型の場合は、適宜読み替えてください。


> この次に計算結果の出力として以下のようなVB6で作られたコードをVB2019に移植したいと思います.
タブ区切りに変更した方が、Excel 等でも編集しやすいので便利そうですが、元のファイルフォーマットに互換性を持たせるなら、FileOpen などといった互換命令を使った方が早いかな…。

元コードが Option Base 0 ではなく Option Base 1 である場合は、配列の添字を調整してください。

Dim fno As Integer = FreeFile()
Dim filePath As String = $"C:\Users\mycomp\Desktop\{TextBox1.Text}.txt"
FileOpen(fno, filePath, OpenMode.Output)
PrintLine(fno, BNX + 1, BNZ + 1, DX, DZ)
Dim X1 = Xmin
For XX As Integer = 1 To BNX + 1
    Dim Z1 = Zmin
    For I As Integer = 1 To BNZ + 1
        PrintLine(fno, X1, Z1, MUD1(I), MUD2(I))
        Z1 += DZ
    Next I
    X1 += DX
Next XX
FileClose(fno)
fno = 0


さて上記では、出力したファイルを読み取るための VB6 や VBA が残っている可能性を考慮し、ファイルフォーマットを維持するために FreeFile / FileOpen / PrintLine / FileClose という互換命令を使いました。

しかし本来であれば FileOpen 等は非推奨となっている命令であり、 .NET Compact Framework や .NET Core や .NET 5 / .NET 6 等の環境では使用できない代物となっています。将来性を考えると、このままの移植を続ける事はあまりお奨めしません。(移植作業が今回限りであり、今後はもう次世代環境への移植は行わないというのであれば、互換命令に頼るのもアリですが)

ファイルの読み取り側も .NET 側に移行する予定であれば、フォーマットを維持する必要もなくなるので、よりプログラムから扱いやすい形式のファイルフォーマット(タブ区切りテキスト、CSV、XML、JSON 等)に切り替えることも検討してみると良いかもしれません。
投稿者 はち  (社会人) 投稿日時 2021/10/24 13:05:41
魔界の仮面弁士様
早速のご返答ありがとうございます.これまでご教示いただきましたことを参考にプログラムを完成させました.エラーが数多くでたのですが,解決策を選択した結果5つのエラーのみとなりました.これにつきましてどのように対応したらよいかご教示なにとぞよろしくお願いいたします。

①BC30481 'Class' ステートメントの終わりには、対応する 'End Class' を指定しなければなりません。 1行目
②BC30001 名前空間では有効でないステートメントです。  1行目
③BC30506 Handles 句には、それを含む型または基本型の 1 つで定義された WithEvents 変数が必要です。 1行目
④BC30001 名前空間では有効でないステートメントです。  5行目
⑤BC30506 Handles 句には、それを含む型または基本型の 1 つで定義された WithEvents 変数が必要です。 5行目

Private Sub Button1_Click(sender As Object, e As EventArgs, Label2 As Object, ListBox1 As ObjectHandles Button1.Click
    Label2.Text = ListBox1.Items(ListBox1.SelectedIndex)
End Sub

Private Sub Button2_Click(sender As Object, e As EventArgs, Label2 As Object, TextBox2 As Object, TextBox3 As Object, TextBox4 As Object, TextBox5 As Object, TextBox6 As Object, TextBox7 As Object, TextBox1 As ObjectHandles Button2.Click
    'Dim X(20000000) As Single, Y(20000000) As Single, UD1(20000000) As Single, UD2(20000000) As Single 
    Dim MUD1(1000) As Single, MUD2(1000) As Single
    Dim DT As Integer
    Dim Xmax As Single, Xmin As Single
    Dim Zmax As Single, Zmin As Single
    Dim TotalX As Single, TotalZ As Single
    Dim X1 As Single, I As Integer, BNX As Integer, BNZ As Integer, Z1 As Single
    Dim Dx As Single, Dz As Single
    Dim UD1SUM As Single, UD2SUM As Single, CT As Integer, XX As Integer

    Dim fileReader As String
    fileReader = My.Computer.FileSystem.ReadAllText(Label2.Text)


    Dim fileName As String = Label2.Text  ' = "C:\test\example.txt"  
    Dim enc = System.Text.Encoding.GetEncoding("Shift_JIS")

    Dim lines As String() = System.IO.File.ReadAllLines(fileName, enc)

    Dim upperIndex As Integer = lines.GetUpperBound(0)
    Dim X(upperIndex), Y(upperIndex), UD1(upperIndex), UD2(upperIndex) As Single

    Dim splitter As Char() = {ControlChars.Tab, " "c, " "c}
    Dim separator = StringSplitOptions.RemoveEmptyEntries
    For rowIndex = 0 To upperIndex
        Dim columns As String() = lines(rowIndex).Split(splitter, separator)

        Single.TryParse(columns(0), X(rowIndex))
        Single.TryParse(columns(1), Y(rowIndex))
        Single.TryParse(columns(2), UD1(rowIndex))
        Single.TryParse(columns(3), UD2(rowIndex))
    Next rowIndex


    '解析対象区域設定 
    Xmin = TextBox2.Text
    Xmax = TextBox3.Text
    Zmin = TextBox4.Text
    Zmax = TextBox5.Text
    TotalX = Xmax - Xmin
    TotalZ = Zmax - Zmin


    Dim fno As Integer = FreeFile()
    Dim filePath As String = $"C:\Users\mycomp\Desktop\{TextBox1.Text}.txt"
    FileOpen(fno, filePath, OpenMode.Output)



    '速度変動のRMS値の計算 


    BNX = TextBox6.Text
    BNZ = TextBox7.Text


    Dx = TotalX / BNX
    Dz = TotalZ / BNZ

    PrintLine(fno, BNX + 1, BNZ + 1, Dx, Dz)

    X1 = Xmin

    For XX = 1 To BNX + 1


        Z1 = Zmin

        For I = 1 To BNZ + 1

            UD1SUM = 0
            UD2SUM = 0
            CT = 0
            For J = 1 To DT

                If X1 - Dx / 2 < X(J) And X(J) < X1 + Dx / 2 Then


                    If Z1 - Dz / 2 < Y(J) And Y(J) < Z1 + Dz / 2 Then
                        CT = CT + 1

                        UD1SUM = UD1SUM + UD1(J)
                        UD2SUM = UD2SUM + UD2(J)

                    End If
                End If
            Next J

            If CT <> 0 Then
                MUD1(I) = UD1SUM / CT
                MUD2(I) = UD2SUM / CT
            End If


            PrintLine(fno, X1, Z1, MUD1(I), MUD2(I))
            Z1 = Z1 + Dz
        Next I

        X1 = X1 + Dx
    Next XX

    FileClose(fno)
    fno = 0

End Sub





投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/10/24 17:43:53
> エラーが数多くでたのですが,解決策を選択した結果5つのエラーのみとなりました.

移植以前の問題として……VB .NET に関する基礎知識が、根本的に不足しているように見えます。
18日の回答にも書きましたが、移植作業というものはある程度の基礎知識を要求する作業です。

それに、算数を学び終わっていない人から数学に関する質問を問われたとしても、
どこから答えれば理解してもらえるのか、中々悩ましいところです。

ということで、移植作業を始める前に、VB に関する何らかの入門者や初級者向けのチュートリアルを
一通り試して、基礎知識をつけておかれることを強くお奨めします。
もちろん、ここのサイト(VB中学校)の講座でも良いでしょう。


それから、移植元となる VB6 に対する経験が十分にあるのかどうかも聞いておきたいところです。
移植元である 『VB6』という言語に関する開発経験は、どの程度ありますか?
VB6 の知識があるのなら、それを踏まえた上で話をすることができるのでは回答しやすいです。

それとも VB6 は初心者、VB.NET に至っては入門者…という段階だったりするのでしょうか。


> これにつきましてどのように対応したらよいかご教示なにとぞよろしくお願いいたします。
では一つずつ。

> ①BC30481 'Class' ステートメントの終わりには、対応する 'End Class' を指定しなければなりません。 1行目

エラーメッセージに『'Class' ステートメントの終わりには』と書かれていますよね。
しかし提示されたコードの 1 行目には、Class という文字列はどこにもありません。

これは変ですね。本当にこのエラーメッセージは、提示されたコードに関して述べられたものでしたか?
もしかして、実際には Form1.vb ではなく、Form2.vb や Module1.vb などといった
他のファイルに対するエラーメッセージなのではありませんか?

いずれにせよ、Windows Form アプリケーションを作成しているのであれば、
最初の時点では、Form1 のコード中に Class ステートメントがあったはずです。
プロジェクトを作成した初期状態で下記のように、Class ステートメントで始まり、
End Class ステートメントで終わるコードが用意されます。
Public Class Form1

End Class


しかし投稿いただいたコードには、Class 宣言や End Class がどこにもありません。

あえて掲示板に記載していないというだけならば良いのですが、エラーメッセージを見る限り、
少なくとも End Class の存在が VB に認識されていない状態であることが分かります。
(おそらくはファイルの 1 行目には Class があるが、対応する End Class が失われている状態)

本当にメソッド (Sub プロシージャーあるいは Function プロシージャーのこと)を
ファイルの先頭に記述しているのだとしたら、Class~End Class の宣言を復活させてください。

「If に対する End If」「For に対する Next」「Sub に対する End Sub」のように、
「Class に対する End Class」が正しく存在していることを確認しましょう。
それが修正されれば、①のエラーは無くなるはずです。


> ②BC30001 名前空間では有効でないステートメントです。  1行目
これは提示されたコードの 1 行目の「Private Sub Button1_Click~」のことですね。

メソッド (Sub プロシージャーあるいは Function プロシージャーのこと)を記述する場所は、
「Class 何某~End Class」ブロックや、「Module 何某~End Module」などの中に限られます。

VB6 の頃とは異なり、ファイルの直下に Sub プロシージャー等を記載することはできません。
上記①でも書きましたが、メソッドはきちんと Class ブロック内に収めるようにしましょう。


> ③BC30506 Handles 句には、それを含む型または基本型の 1 つで定義された WithEvents 変数が必要です。 1行目
Private Sub Button1_Click~~の行を、手作業で記述したり、後から編集してしまっているようですね?

フォームにボタンを貼ってそれをダブルクリックした場合、イベントハンドラーとして自動的に
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

End Sub
というコードが記載されるはずです。しかし提示されたコードでは、引数の数がまったく違っています。
イベントハンドラーとなるメソッドは、原則として 2 つの引数をもつものとして定められており、それぞれの引数の型も決められています。
この定義部は勝手に変更しないようにしましょう。

なお、最初にあった「Public Class Form1~End Class」のコードをすべて削除していた場合、その状態で
コントロールをダブルクリックしても、イベントハンドラーが期待通りに生成されないなどの問題を引き起こすことがあります。

また、VB2019 のバージョン v16.10.0~v16.10.2 においては、正しい操作手順を踏んでいたとしても、
イベントハンドラーが正しく生成されないという不具合がありました。こちらの問題は、現在のバージョンでは
正しく生成されるように修正されていますので、Visual Studio には最新版の更新プログラムを適用しておきましょう。


②と③の問題は、そのまま ④と⑤が指す Private Sub Button2_Click の 5 行目にも当てはまるので省略。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/10/24 17:49:40
> Label2.Text = ListBox1.Items(ListBox1.SelectedIndex)
これはおかしいですね。
左辺の Text プロパティは String 型のプロパティですが、右辺は Object 型であるため、型が間違っています。

この場合は、
 Label2.Text = ListBox1.GetItemText(ListBox1.SelectedItem)
もしくは
 Label2.Text = ListBox1.SelectedItem?.ToString()
のように記述します。

なお、『ListBox1.Items(ListBox1.SelectedIndex)』と『ListBox1.SelectedItem』は
同じ意味になります。今回は SelectedItem の方がスマートでしょう。



> Dim fileReader As String
> fileReader = My.Computer.FileSystem.ReadAllText(Label2.Text)

> Dim fileName As String = Label2.Text  ' = "C:\test\example.txt"  
> Dim enc = System.Text.Encoding.GetEncoding("Shift_JIS")
> Dim lines As String() = System.IO.File.ReadAllLines(fileName, enc)

fileReader では「UTF-8 として読み込んだテキスト全文」が取得され、
lines には「Shift_JIS として配列に読み込んだテキスト全文」が取得されています。

同じファイルを 2 回に分けて別々の変数に読み込んでいるのは、何のためですか?
どちらか一方あれば事足りるように思うのですが。


> Xmin = TextBox2.Text
> Xmax = TextBox3.Text
> Zmin = TextBox4.Text
> Zmax = TextBox5.Text
これも本来はおかしいですね。

代入式の左辺にある変数はいずれも Single 型なのに、
右辺にある Text プロパティは String 型なので、型が一致していません。

現状は特にエラーとして検出されていないかもしれませんが、
コンパイル設定を厳密にした場合、このコードはエラーまたは警告として扱われます。
また VB6 や VBA であっても、このような代入はあまり望ましくありません)


この場合は、
 Xmin = CSng(TextBox2.Text)
とします。ただしこれでも、TextBox の内容が空だったり漢字だったりすれば変換エラーになるので、
 Single.TryParse(TextBox2.Text, Xmin)
とするとより丁寧かと思います。
TryParse を使って変換すれば、数値化できないときにはエラーとするのではなく、0 として扱われます。

> BNX = TextBox6.Text
> BNZ = TextBox7.Text
これも、Integer 型変数に String 型を代入していることになってしまうので、
 BNX = CInt(TextBox6.Text)
あるいは
 Integer.TryParse(TextBox6.Text, BNX)
ですね。


なお、「文字列を数値に変換できなかった場合に、単に 0 として扱う動作では困る」というときには、If 文でのチェックを併用します。
Dim x As Single
If Single.TryParse(TextBox1.Text, x) Then
    'Single 型への変換に成功した場合はここを通り、 
    'x には変換された数値がセットされた状態となる。 
Else
    'Single 型への変換に失敗した場合にはここを通り、 
    'x には 0 がセットされた状態となる。 
End If




上記のような型の違いを厳密にチェックするには、ファイルの先頭に以下の一文を追加します。
Option Strict On


とはいえ、現時点でこれを入れてしまうことはまだお奨めしません。
おそらく、エラー検出箇所が大量に出てきてしまい、かえって混乱してしまうでしょうから。

しかし、プログラムが一通り動き出す段階にまで進んだ後は、
上記の宣言を付与した状態で動かしてみて、型の指定ミスが無いかという点についても、
きちんと検出しておくことをお奨めします。
投稿者 はち  (社会人) 投稿日時 2021/10/25 13:39:28
魔界の仮面弁士様
 早速の対応いただきありがとうございます。
私のVB6の熟練程度としては,初歩の段階です.ファイルを読み込んで,四則演算をした結果をファイルに書きこむといった単純なプログラムが書ける程度です.VB2019につきましては,入門書を数冊買ったばかりの全くの初心者です.
 これまでVB6で作成したプログラムをWindws10で使用してきましたが突然動作が遅くなり計算時間がかなりかかるようになったため,VB2019への移行を考えた次第です.
 魔界の仮面弁士様には何度もご指導いただき感謝申し上げます.当方でいろいろ試みましたが,どうしても正常に作動しません.なかなか時間が取れませんが,1からVB2019を勉強するしかないと思っております.これまで丁寧に対応いただきましたのに,申し訳ありません.今回で解決とさせていただきたいと思います.お世話になりました.申し訳ありません.