[[20150210141643]] 『テキストファイルの読み込み』(yamasaki akari) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『テキストファイルの読み込み』(yamasaki akari)

前の方とニックネームがかぶってしまいましたので、変更しました。

VBA勉強中の者です。
ネットで探した下記のコードで、テキストファイルの読み込みをしています。

読込ファイル

 -54.51659 12.7292 16.4232
 -45.72083 13.6852 -11.2325
読込後の値 

 -54.51659 \12.73 \16.42
 -45.72083 \13.69 \-11.23
となってしまいます。
どなたか、御教示お願いします。 

Sub READ_TextFile()

    Const cnsTITLE = "テキストファイル読み込み処理"
    Const cnsFILTER = "txt形式ファイル (*.txt),*.,全てのファイル(*.*),*.*"
    Dim xlAPP As Application
    Dim intFF As Integer
    Dim strFileName As String
    Dim vntFileName As Variant
    Dim X(1 To 5) As Variant
    Dim GYO As Long
    Dim lngREC As Long

    Set xlAPP = Application
    xlAPP.StatusBar = "読み込むファイル名を指定して下さい。"
    vntFileName = xlAPP.GetOpenFilename(FileFilter:=cnsFILTER, _
                                        Title:=cnsTITLE)
    If VarType(vntFileName) = vbBoolean Then Exit Sub
    strFileName = vntFileName

    intFF = FreeFile
    Open strFileName For Input As #intFF
    GYO = 0
    Do Until EOF(intFF)
        lngREC = lngREC + 1
        xlAPP.StatusBar = "読み込み中です....(" & lngREC & "レコード目)"
        Input #intFF, X(1), X(2), X(3)
        GYO = GYO + 1
        Range(Cells(GYO, 1), Cells(GYO, 5)).Value = X
    Loop
    Close #intFF
    xlAPP.StatusBar = False
    MsgBox "ファイル読み込みが完了しました。" & vbCr & _
        "レコード件数=" & lngREC & "件", vbInformation, cnsTITLE
End Sub

< 使用 Excel:Excel2010、使用 OS:Windows7 >


 B、C 列のセルの書式が通貨になっていないでしょうか。
(Mook) 2015/02/10(火) 14:33

Dim X(1 To 5) As Double
にしたら通貨ではなくなりました
データが無い列には0が入ってしまいます。
理由についてはもう少し調べます
(デイト) 2015/02/10(火) 14:40

Mookさん
読み込む前は標準で、読込後の¥の付いたセルは通貨になっています。
デイトさん
宜しくお願いします。

(yamasaki akari) 2015/02/10(火) 14:48


 こんにちは

 テキストファイルの仕様については詳しくなく、また、その I/O についても詳しくありませんが
 ステップ実行をしながらローカルウィンドウで確認しますと、
 問題なく表示される1列目については、そのデータ型が Variant/Decimal になっていますが、2列目、3列目については
 値は、丸められずに保持されているものの、データ型として、Cariant/Currency になっています。
 で、結果として、質問文にあるように、\12.73 等になっている。しかも、実際のセルの値も、12.73 になってしまってますね。

 ここを、コード内で、無理やり Decimal型に変換してやりますとセルの値としては 12.7292 等、正常になります。
 ただ、表示書式は通貨型のままなので、、表示としては \12.73 。

 正しい扱いかどうかはわかりませんは、値を強引にDecimal型にしたうえで表示書式を標準にしてやれば
 とりあえずは、「正しく」処理されますね。

 Sub READ_TextFile()

    Const cnsTITLE = "テキストファイル読み込み処理"
    Const cnsFILTER = "txt形式ファイル (*.txt),*.,全てのファイル(*.*),*.*"
    Dim xlAPP As Application
    Dim intFF As Integer
    Dim strFileName As String
    Dim vntFileName As Variant
    Dim X(1 To 5) As Variant
    Dim GYO As Long
    Dim lngREC As Long

    Set xlAPP = Application
    xlAPP.StatusBar = "読み込むファイル名を指定して下さい。"
    vntFileName = xlAPP.GetOpenFilename(FileFilter:=cnsFILTER, _
                                        Title:=cnsTITLE)
    If VarType(vntFileName) = vbBoolean Then Exit Sub
    strFileName = vntFileName

    intFF = FreeFile
    Open strFileName For Input As #intFF
    GYO = 0
    Do Until EOF(intFF)
        lngREC = lngREC + 1
        xlAPP.StatusBar = "読み込み中です....(" & lngREC & "レコード目)"
        Input #intFF, X(1), X(2), X(3)
        '★以下強引に
        X(1) = CDec(X(1))
        X(2) = CDec(X(2))
        X(3) = CDec(X(3))

        GYO = GYO + 1
        With Range(Cells(GYO, 1), Cells(GYO, 5))
            .Value = X
            '★以下強引に
            .NumberFormatLocal = "G/標準"
        End With
    Loop
    Close #intFF
    xlAPP.StatusBar = False
    MsgBox "ファイル読み込みが完了しました。" & vbCr & _
        "レコード件数=" & lngREC & "件", vbInformation, cnsTITLE
 End Sub

(β) 2015/02/10(火) 14:50


βさん有難う御座います。
コード勉強させていただきます。
(yamasaki akari) 2015/02/10(火) 14:55

 かぶったが。

 12.7292、16.4232
 は小数部が4桁なので内部形式が通貨型のVariantとして扱える
 -54.51659
 は小数部が5桁以上なので内部形式がシングルのVariantとして扱っているのでは?

 それをセルに入れる際にEXCELが内部型に適した表示形式にしているのでは?
 なぜ、表示形式「通貨」で小数点以下が二けたになっているかはわからないが。
(ねむねむ) 2015/02/10(火) 14:59

 単純な話ではなかったようですね。
 無責任な発言で失礼しました。

 βさんのように、書式を制御するのが手っ取り早そうですね。
(Mook) 2015/02/10(火) 15:07

解決はしたようなので一応テスト結果を報告
TypeName(X(1))でタイプ型を調べてみました

Variant 小数点なし Integer型
Variant 小数点4ケタまで Currenry型
Variant 小数点5ケタ以降 Decimal型

となりました。
(デイト) 2015/02/10(火) 15:17


皆様、御親切に有難う御座いました。
勉強になりました。
(yamasaki akari) 2015/02/10(火) 15:23

 すまない、βさんの書き込みをきちんと読んでいなかった。

 私の書き込み
 >-54.51659
 >は小数部が5桁以上なので内部形式がシングルのVariantとして扱っているのでは?

 βさんの書き込み
 >問題なく表示される1列目については、そのデータ型が Variant/Decimal になっていますが

 シングルではなく十進型ですね。
(ねむねむ) 2015/02/10(火) 15:24

value2を調べよう
(日捲り熊五郎) 2015/02/10(火) 15:43

 不思議なことがあるもんですね

 最初から Double型の配列に読ませれば、だいじょうぶですけど
 Sub READ_TextFile_AsDouble()
    Const cnsTITLE = "テキストファイル読み込み処理"
    Const cnsFILTER = "txt形式ファイル (*.txt),*.,全てのファイル(*.*),*.*"
    Dim xlAPP As Application
    Dim intFF As Integer
    Dim strFileName As String
    Dim vntFileName As Variant
    Dim X(1 To 3) As Double
    Dim GYO As Long
    Dim lngREC As Long
    Set xlAPP = Application
    xlAPP.StatusBar = "読み込むファイル名を指定して下さい。"
    vntFileName = xlAPP.GetOpenFilename(FileFilter:=cnsFILTER, _
                                        Title:=cnsTITLE)
    If VarType(vntFileName) = vbBoolean Then Exit Sub
    strFileName = vntFileName
    intFF = FreeFile
    Open strFileName For Input As #intFF
    GYO = 0
    Do Until EOF(intFF)
        lngREC = lngREC + 1
        xlAPP.StatusBar = "読み込み中です....(" & lngREC & "レコード目)"
        Input #intFF, X(1), X(2), X(3)
        GYO = GYO + 1
        Cells(GYO, 1).Resize(, 3).Value = X
    Loop
    Close #intFF
    xlAPP.StatusBar = False
    MsgBox "ファイル読み込みが完了しました。" & vbCr & _
        "レコード件数=" & lngREC & "件", vbInformation, cnsTITLE
 End Sub 
(kanabun) 2015/02/10(火) 19:27

 Inputステートメント、N88Basicの時代からあるステートメントです!!

 この時代、Variant型なんて変数の型はありません。ですから、新たに作った仕様なのでしょうねえ
 値を文字列などを使って大きくすると、Doubleにも型は変化しますねえ。値によって型が変化してくれると
 便利なこともあるのかなあ?

 昔は、変数は宣言なんてことはしませんでした。
 今の Dimは、配列の宣言にだけ使っていました。

 変数を予め宣言しなくてもよかったのですが、変数の記述で型は決まっていました。

 hensu は、単精度、 hensu$は、文字列 hensu% Integer etc

 よって、Inputステートメントに型が未定の変数が指定されることはありませんでした。

 Binaryモードだと、書きこんだ型と違う型の変数で読み込もうとしても、
 顕著に結果(きちんと値が取得できない)が現れます。

 Inputステートメント、最近はあまり使いませんが、昔はよく使いました。
 その際、書きこむ型と読み込む型は、同じにしていました。

 今回のファイルデータのデータ内容も 
 小数を含むデータがあるとわかっているなら、
 kanabunさんのDouble変数のように型を明示した変数を使うことに賛成ですね!!

 ところで このInputステートメントの代わりのメソッドって何かありましたっけ?

 FSOやADOのStreamに???

(ichinose ) 2015/02/11(水) 08:49


ところで このInputステートメントの代わりのメソッドって何かありましたっけ?
FSOやADOのStreamに???

 おはようございます。
 私自身は使ったことはないですが、FSO で

 OpenTextFile
 GetFile の OpenAsTextStream(IOMode:=ForReading)
 で、AtEndOfStream までの ReadLine

 こんな処理ができるようです。

(β) 2015/02/11(水) 09:26


 Inputステートメントは Write#ステートメントで書き出したテキストを
 項目ごとに分けて読むのに適してましたね。
 たとえば、以下のような テキストファイル(これは Write#で書き出した
 ものではないのでカンマ区切りではありませんが)。
 '-----------------------------------------------------------
 "001" -54.51659 12.7292 16.4232
 "002" -45.72083 13.6852 -11.2325
 "1st Step" 12.7292 16.4232 -54.51659
 "2nd Step" 13.6822 -11.2325 -45.72083
 "3rdDim" 18.6754 13.2456 -22.4455
 '-----------------------------------------------------------
 一行はスペースで区切られて4項目ありますが、第1列目はダブルクォーテーションつきの文字列です。
 Input# は ダブルクォーテーションで囲まれた部分を文字列データと認識してくれますので、
 ダブルクォート付きのデータから "" を取り除いて中身だけ取り出すのに適しています。
 ただし、
 これを以下のように
 > Dim X(1 To 4) As Variant
 とVariant型で読むと、Excelのシートに貼り付ける際に "001"などの前ゼロが消えてしまいます。
 Sub Input_Ck1()
  Dim Filename

  Filename = Application.GetOpenFilename("Text,*.txt")
  If VarType(Filename) = vbBoolean Then Exit Sub

  Dim io%
  Dim X(1 To 4) As Variant
  Dim GYO As Long

  With Worksheets
      .Add After:=.Item(.Count)   '新規シートの挿入
  End With
  io = FreeFile()
  Open Filename For Input As io
  Do Until EOF(io)
      Input #io, X(1), X(2), X(3), X(4)
      GYO = GYO + 1
      Cells(GYO, 1).Resize(, 4).Value = X
  Loop
  Close io

 End Sub
 このような結果になります。
 '-----------------------------------------------------------
 1     -54.51659      \12.73     \16.42
 2     -45.72083      \13.69     \-11.23
 1st Step \12.73      \16.42    -54.51659
 2nd Step \13.68      \-11.23   -45.72083
 3rdDim   \18.68      \13.25    \-22.45
 '-----------------------------------------------------------

 各項目のデータ型が分っていれば、データ型を指定して読み込むと当初の型で貼り付けれます。
 Sub Try1()
  Dim Filename

  Filename = Application.GetOpenFilename("Text,*.txt")
  If VarType(Filename) = vbBoolean Then Exit Sub

  Dim io%
  Dim s As String
  Dim X(1 To 3) As Double
  Dim GYO As Long

  With Worksheets.Add(After:=Worksheets(Worksheets.Count)) '新規シートの挿入
      .Columns("A").NumberFormat = "@"                    'A列は文字列型
  End With
  io = FreeFile()
  Open Filename For Input As io
  Do Until EOF(io)
      Input #io, s, X(1), X(2), X(3)
      GYO = GYO + 1
      With Cells(GYO, 1)
          .Value = s
          .Offset(, 1).Resize(, 3).Value = X
      End With
  Loop
  Close io
 End Sub
 結果
 '-----------------------------------------------------------
 001       -54.51659     12.7292     16.4232
 002       -45.72083     13.6852    -11.2325
 1st Step  12.7292       16.4232    -54.51659
 2nd Step  13.6822      -11.2325    -45.72083
 3rdDim    18.6754       13.2456    -22.4455
 '-----------------------------------------------------------
 .
(kanabun) 2015/02/11(水) 11:30

 失礼します。

 もともとの質問者さんのコードに

 Range(Cells(GYO, 1), Cells(GYO, 5)).Value = X

 と、5項目を転記するというのがありましたので、X(4) と X(5) に対する扱い上
 すべてを As Double とはできないんだろうなということと、
 (デイト)さんの 2015/02/10(火) 14:40 のコメントの様に、X(1)等が空白のものに、 0 が入ってしまうのは
 不都合なんだろうな(そういった項目があるかどうか、質問者さんからのコメントがないのでわかりませんが)
 という思いが、皆さんにあったように思いますが。

(β) 2015/02/11(水) 16:07


 βさん、情報ありがとうございます。ただ、

 >OpenTextFile
 >GetFile の OpenAsTextStream(IOMode:=ForReading)
 >で、AtEndOfStream までの ReadLine

 これは、 Line Input ステートメントと同等ですね!!

 何故、ふとそんなこと(このInputステートメントの代わりのメソッド)を考えたかと言いますと、

 前投稿でも申しましたが、Inputステートメントは Dos時代に使っていたBasicでは、よく使いましたが、
 VBAでは、Dos時代のファイルを扱うときに使うくらいなんです。
 で、違和感を持ったわけで 今時? というやつです。

 これも前投稿で申しましたが、このInputステートメントと書き込みでは、Printステートメント
 (kanabunさんは、Writeステートメントなんて記述されていましたが、私は、InputとPrintでの読み書きが
 殆どでした)の組合せで使っていましたが、
 ファイルのデータ構造がはっきりわかったようなものを対象に使っていました。

 (Printステートメントだと文字列の場合は、カンマで区切らなければなりませんでしたが)

 ですから、

 >X(1)等が空白のもの
 数値なのか文字列なのか読み込んでみなればわからない というものには 不向きなんです。

     A   B   C
 1          123.12   222.22
 2   1231   55.222   123.233

 上記のようなデータを Tab区切りのテキスト(*.txt)ファイルで保存したものを
 READ_TextFile_AsDouble を使って読み込むとエラーになります。

 データによって、文字列か数値かはっきりしない場合は、Line Inputで1行読み込んで
 Splitで配列化するよく使われる方法でよいのに・・・。

 なんて思ったのですが、そこでふと最初の(Inputステートメントの代わりのメソッド)が
 頭に浮かんだのです。

 ADOを使ってテキストを読み込めば、Recordsetがそれに当たるかなあ

 x(4) x(5)が不明なら、配列分けたってよいし、Redim増やす方法だってありますけどね!!

 後は、質問者さんに選択してもらいましょうか!!

(ichinose ) 2015/02/11(水) 20:25


 ichinoseさん

 ご教示、ありがとうございました。中途半端なレベルのレスをしてしまったようで、汗、汗です。

 ところで、上で述べましたように、あまり FSO は使わないのですが以下のようなコードを書いて試してみました。
 ファイルは、質問者さんが最初に提示された2行に、12.5678	 空白	11.1234 を加えた3行の タブ区切りのテキストファイル。

 読み込み後の、配列への格納状況を見ますと、すべてが String型でした。
 で、結果、シートには、正しくといいますか、元のテキストファイルと同じ状態で転記されました。

 Sub Test()
    Dim Fso As Object
    Dim txt As Object
    Dim s As String
    Dim v As Variant
    Dim i As Long

    Set Fso = CreateObject("Scripting.FileSystemObject")
    Set txt = Fso.OpenTextFile(Filename:=CreateObject("WScript.Shell").SpecialFolders("desktop") & "\Book1.txt", IOMode:=1)
    With txt
        Do Until .AtEndOfStream
            s = .ReadLine
            v = Split(s, vbTab)
            i = i + 1
            With Range("A" & i).Resize(, 3)
                .Value = v
                .Value = .Value
            End With
        Loop
        .Close
    End With
 End Sub

(β) 2015/02/11(水) 21:43


 う〜ん。忌憚のない意見を言わせてもらえば、

 >          s = .ReadLine
 >          v = Split(s, vbTab)

 .ReadLine は ichinose さんのコメントにあるように、VBA組み込みのステートメントでいえば 
 Line Inputステートメントで、 この動作は 「1行を文字列として読む」動作ですよね?
 一行を文字列として読み込んでから、
 それを vbTab(とか、任意の区切り記号で) 項目に分割(Split) しても、各フィールドのデータ型は
 文字列のままです。

 >                 .Value = .Value

 この一行で、標準書式に直していますが、そもそも、こういう処理方法は、
 Inputステートメントで一項目ずつ 読ませる処理(従って Variant型の変数に格納するときの問題、
 列ごとにデータ型を指定するときの問題、等々)とは 別の次元の話題のような気がします。
 .
(kanabun) 2015/02/11(水) 22:41

 質問者さんのコードの元は
 井上治さんのサイトの
 CSV形式テキストデータの読み込みのサンプル
 http://www.asahi-net.or.jp/~ef2o-inue/vba_o/sub05_110_020.html
 ですよね?

 そこには こんなコメントがあります。
 > (1)変数はCSVの項目数分作成する必要があります。(中略)Variant型にしているのは、
 >   読み込んで収容する値に応じたデータ型になる利点を利用するためです。
 > (2) レコードの読み込みはInputステートメントで行ないます。ファイル番号の次からが
 >    レコードデータを格納する変数で項目数がレコード上の項目数と合っていないとレコードずれを起こします。
 > ※ 項目の属性が一定していない(数値になったり文字になったりする)場合は、
 >   Inputメソッドの前に配列の初期化(Erase X)の行を追加して下さい。
 > XはVariantとしているので、セットする値によって属性のふるまいが変わります。
 .
(kanabun) 2015/02/11(水) 23:00

 kanabunさんへ

 >CSV形式テキストデータの読み込みのサンプル
 >http://www.asahi-net.or.jp/~ef2o-inue/vba_o/sub05_110_020.html

 へえ、井上さんも細かい検証されていますねえ!!

 Csvファイルなら、前投稿でエラーになるデータ例

 > READ_TextFile_AsDouble を使って読み込むとエラーになります。

 は、正しく読み込まれます。

 テキストファイルを読み込んでシートに表示するが目的なら、他に方法があります。
 私は、ADOなんて申し上げましたが、Excelには、テキストデータの取り込みなんて方法もありますから、
 試してみてください。

 Inputステートメントに Variant型の変数を使う方法 私は、今は、利用メリットがでてきませんし、
 使うことがあるだろうか? という思いですが、
 知ってさえいれば、何かの役に立つかもしれません。

 新しい発見がありました、この質問に感謝します。

(ichinose ) 2015/02/12(木) 05:16


 kanabunさん 
 ichinoseさん

 度重なるご教示深謝です。

 今回のトピ、私自身はテキストファイルからの取込みというポイントよりも、扱われるデータのデータ型の相違ということが
 興味深く、他の皆さんからのコメントも含めて、勉強になったことが多かったです。

 kanabunさんから紹介いただいた、井上さんのコメント、
 「読み込んで収容する値に応じたデータ型になる利点を利用するためです」
 つまり、12.7292 という文字列に対しては、「通貨型(Currency)」が、値に応じたデータ型であると、INPUT は、そう判断しているわけですね。
 これに対して、私が確認した限りでは、"12.7292" という文字列を .Value = .Value で 変換すると、
 エクセルは この文字列に応じたデータ型を「倍精度浮動小数点数型(Double)」と判断していますね。
 エクセル上で、12.7292 とタイプして入力した場合も、エクセルは、この文字列にふさわしいデータ型をDouble型だと判断しますね。

 どちらが正しい判断なのかはわかりませんが、我々利用者からみれば、この点に限っては、エクセルの判断がありがたいと、そう思いました。

 追伸:ichinoseさんからアドバイスがあった外部データ取り込み、初めて試行しましたが
    便利なものですねぇ! ありがとうございます。

(β) 2015/02/12(木) 08:18


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.