Excel (VBA)

Excel VBAに関するフォーラムです。
  • 解決済みのトピックにはコメントできません。
このトピックは解決済みです。
質問

 
(指定なし : 指定なし)
CSVデータを正しく配列に格納し、数値を正しく転記する方法について
投稿日時: 23/03/30 21:27:51
投稿者: TATSUYA.ich

下記コードはCSVファイルを読み込み、そのデータを配列に格納した上で、所定の配列のデータを所定の列に記載するためのコードです。
  
しかしながら問題点が2つあります。
  
一つは、1行を読み込んだ際、分割して配列に格納しますが、この分割した場合の列の要素数が、カンマ区切りか、空白列を正しく読み込めないのか理由は不明ですが、正しく格納できません。
  
そのため、行によって列数が異なることとなっており、現状無理矢理Redimで配列を修正して読み込んで書き込みをしていますが、結局書き込みをするときにうまく所定の位置に書き込みができない状態です。
  
また、2つ目の問題点としては、数値の3345685496等の値を転記しようとしたところ、3345600000のように数字が文字化けする現象が起きており、ADODB.StreamでUTF8へ指定をしてもうまく転記することができておりません。
  
上記のように、いかなる場合にもCSVファイルの列数を正しく認識して配列に格納する方法及び数値を正しく転記する方法をご教示くださいませんでしょうか。
  
よろしくお願いします!
  
Sub import_UTF8_Sales_Data()
  
Dim tFolder, tFile
Dim ws As Worksheet
Dim lastRow As Long
Dim max_n As Long
Dim max_items As Long
Dim buf As String, tmp As Variant, ary() As Variant
Dim i As Long, n As Long, val As Long
Dim strLine As String
   
tFolder = ThisWorkbook.Path & "\データ\"
tFile = Dir(tFolder & "〇〇.csv")
  
Set ws = Worksheets("売上管理表")
lastRow = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
max_n = CreateObject("Scripting.FileSystemObject").OpenTextFile(tFile, 8).Line 'ファイルの行数取得
  
'Open tFile For Input As #1
  
    'Line Input #1, buf
    'max_items = UBound(Split(buf, ","))
      
'Close #1
          
ReDim ary(0 To max_n, 0 To max_items) As Variant
  
Dim adoSt As Object
Set adoSt = CreateObject("ADODB.Stream")
          
    With adoSt
        .Charset = "UTF-8" 'Streamで扱う文字コートをutf-8に設定
        .Open 'Streamをオープン
        .LoadFromFile (tFile)
          
        n = 0
          
        Do Until .EOS
              
            strLine = .ReadText(adReadLine)
                  
                If strLine <> "" And n <= max_n Then
                  
                    tmp = Split(Replace(replaceColon(strLine), """", ""), ":")
                      
                    If n > UBound(ary, 1) Then
                                      
                        ReDim Preserve ary(0 To n, 0 To max_items) As Variant ' 配列の再設定
                          
                    End If
      
                    If UBound(tmp) <> UBound(ary, 2) Then
                                         
                           ReDim Preserve ary(0 To max_n, 0 To UBound(tmp)) As Variant ' 配列の再設定
                             
                    End If
                         
                    For i = 0 To UBound(tmp)
                         
                           ary(n, i) = tmp(i)
                             
                    Next i
                      
                End If
                  
                n = n + 1
                  
          Loop
              
        .Close
      
    End With
      
    MsgBox n
      
    For i = 2 To max_n
      
       ws.Cells(lastRow + 1, 1).Value = ary(i, 2)
       ws.Cells(lastRow + 1, 2).Value = ary(i, 11)
       ws.Cells(lastRow + 1, 3).Value = ary(i, 22)
       ws.Cells(lastRow + 1, 4).Value = ary(i, 23)
       ws.Cells(lastRow + 1, 9).Value = ary(i, 27)
       ws.Cells(lastRow + 1, 10).Value = ary(i, 28)
       ws.Cells(lastRow + 1, 11).Value = ary(i, 38)
       ws.Cells(lastRow + 1, 12).Value = ary(i, 46)
        
      lastRow = lastRow + 1
        
    Next i
  
End Sub
 

回答
投稿日時: 23/03/31 06:14:26
投稿者: simple

CSVデータの項目数が不定個(らしい)ですが、
OpenステートメントやADODB.Streamにこだわらず、他の方法を使ってはどうでしょうか。
トリッキーなデータになっているとしたら、
それを自前で配列処理するのは、労多くしてとなりそうな印象です。
 
https://www.tipsfound.com/vba/18014
に紹介されているQueryTablesを使う方法はどうですか?
 
Excelのバージョンが記載されていないので(下記*)具体的に言えませんが、
インタラクティブに操作できるメニューがExcelに用意されているはずです。
(versionによっては「テキストから(レガシ)」を使う必要があるかもしれません。)
 
・マクロ記録もできます(上記記事のコードを少し修正しただけでも使えるとは思いますが。)
・文字コード指定や、
・書き込み先の指定
・書き込みセルの書式指定(日付データや、頭に0がついた数字の扱いなど)
なども簡単にできます。
 
いずれにせよ、どんな方法でもいったんExcelに展開して、
項目数がどうなっているかを調べたらいかがですか?
(データそのものが間違っている可能性もあります。)
 
* 忘れずに書いてください。それが関係する議論も多いですよ。

回答
投稿日時: 23/03/31 06:17:22
投稿者: simple

追記です。
前スレッドでもそうでしたが、Redimを使って配列を拡大する処理がありますが、
それが可能なのは、最後の次元についてだけです。一次元方向に広げることはできません。

回答
投稿日時: 23/03/31 10:31:12
投稿者: WinArrow

simple さんの引用:
追記です。
前スレッドでもそうでしたが、Redimを使って配列を拡大する処理がありますが、
それが可能なのは、最後の次元についてだけです。一次元方向に広げることはでき
ません。

これに対する補足です。
>一次元方向に広げることはできません。。
「拡大」も「縮小」もできません。(エラーになるはずです)

回答
投稿日時: 23/03/31 17:07:12
投稿者: WinArrow

前トピのコードの中におかしなところがあります。
今回のコードにも同じコードが・・・
 

引用:
Line Input #1, buf
            If buf <> "" And n <= max_n Then
                tmp = Split(buf, ",")
                If n > UBound(ary, 1) Then
                    ReDim Preserve ary(0 To n, 0 To max_items) As Variant ' 配列の再設定
                End If
 
  

 
CSVをBUFに読み込んで、tmpに項目分解しているが、
変数「n」の値は変化していませんよね?
従って
If n > UBound(ary,1) Then
が成立していないから、配列再設定もスルーしています。
本来ならば、配列再設定でエラーになるはず・・・・
 
CSVのアクセスを変えるのではなく、基本的な部分をキチンと見直しましょう。
ステップ実行すれば、分かるはずです。

回答
投稿日時: 23/04/01 10:01:16
投稿者: WinArrow

若しかして、掲示したコードは、手入力ですか?
 
何カ所か、よくわからないコードが見受けられます。
 
問題点1

引用:

tFile = Dir(tFolder & "〇〇.csv")


引用:

max_n = CreateObject("Scripting.FileSystemObject").OpenTextFile(tFile, 8).Line 'ファイルの行数取得

➁では➀で取得した「tFile」を使っていますが、
➁では、フルパスを指定すべきところ、ファイル名だけになっています。
「tFile」は、別のところでも使っているので、確認が必要です。
「tFile」で、動いているとすれば、@は間違いということになりませんか?
 
問題点2

引用:

        Do Until .EOS

.EOS
は、間違いですよね?
 
問題点3

引用:

                    tmp = Split(Replace(replaceColon(strLine), """", ""), ":")

このコードは、何をしているのでしょうか?
 
問題4
D
引用:

                    If n > UBound(ary, 1) Then
                                       
                        ReDim Preserve ary(0 To n, 0 To max_items) As Variant ' 配列の再設定
                           
                    End If

このコードは、不要と思います。
max_nはファイルの中のデータ件数のはずです。
しかし、aryの一次元は「0」から始まっているので、実際のデータより1件多い。
もし、これが実行されると、実際にはエラーになります。
「n」を変更しているところを探すべきです。
掲示のコード(手入力による)が実際のコードと食い違いあれば、別の話です。

投稿日時: 23/04/01 13:55:11
投稿者: TATSUYA.ich

simple さんの引用:
CSVデータの項目数が不定個(らしい)ですが、
OpenステートメントやADODB.Streamにこだわらず、他の方法を使ってはどうでしょうか。
トリッキーなデータになっているとしたら、
それを自前で配列処理するのは、労多くしてとなりそうな印象です。
 
https://www.tipsfound.com/vba/18014
に紹介されているQueryTablesを使う方法はどうですか?
 
Excelのバージョンが記載されていないので(下記*)具体的に言えませんが、
インタラクティブに操作できるメニューがExcelに用意されているはずです。
(versionによっては「テキストから(レガシ)」を使う必要があるかもしれません。)
 
・マクロ記録もできます(上記記事のコードを少し修正しただけでも使えるとは思いますが。)
・文字コード指定や、
・書き込み先の指定
・書き込みセルの書式指定(日付データや、頭に0がついた数字の扱いなど)
なども簡単にできます。
 
いずれにせよ、どんな方法でもいったんExcelに展開して、
項目数がどうなっているかを調べたらいかがですか?
(データそのものが間違っている可能性もあります。)
 
* 忘れずに書いてください。それが関係する議論も多いですよ。

 
Simple様
 
ご回答ありがとうございます!!
 
大変助かります。。
 
お返事が遅くなり申し訳ございません。
 
また、エクセルのバージョンなどについても申し訳ございませんでした。
 
Microsoft office personal2019でございました。
 
頂いたクエリテーブルのコードですが、今から試してみたいと思います。
 
いきなり読み込むよりも一度展開したほうがやりやすいですよね!

投稿日時: 23/04/01 13:56:31
投稿者: TATSUYA.ich

WinArrow さんの引用:
simple さんの引用:
追記です。
前スレッドでもそうでしたが、Redimを使って配列を拡大する処理がありますが、
それが可能なのは、最後の次元についてだけです。一次元方向に広げることはでき
ません。

これに対する補足です。
>一次元方向に広げることはできません。。
「拡大」も「縮小」もできません。(エラーになるはずです)

 
Simple様、WinArrow様
 
 
お返事ありがとうございます。。
 
そうなんですね、今回は2次元方向に広げる処理が中心になっているかと思います。
 

投稿日時: 23/04/01 13:59:28
投稿者: TATSUYA.ich

WinArrow さんの引用:
前トピのコードの中におかしなところがあります。
今回のコードにも同じコードが・・・
 
引用:
Line Input #1, buf
            If buf <> "" And n <= max_n Then
                tmp = Split(buf, ",")
                If n > UBound(ary, 1) Then
                    ReDim Preserve ary(0 To n, 0 To max_items) As Variant ' 配列の再設定
                End If
 
  

 
CSVをBUFに読み込んで、tmpに項目分解しているが、
変数「n」の値は変化していませんよね?
従って
If n > UBound(ary,1) Then
が成立していないから、配列再設定もスルーしています。
本来ならば、配列再設定でエラーになるはず・・・・
 
CSVのアクセスを変えるのではなく、基本的な部分をキチンと見直しましょう。
ステップ実行すれば、分かるはずです。

 
WinArrow様
 
基本がなっておらず申し訳ございません。。
 
Nが変化して行数を超えた場合に、上記処理で対応できないかなと思って記述したのですが、1次元は拡大も縮小もできないということを知らずに記述しておりました。
 

投稿日時: 23/04/01 15:37:14
投稿者: TATSUYA.ich

WinArrow さんの引用:
若しかして、掲示したコードは、手入力ですか?
 
何カ所か、よくわからないコードが見受けられます。
 
問題点1

引用:

tFile = Dir(tFolder & "〇〇.csv")


引用:

max_n = CreateObject("Scripting.FileSystemObject").OpenTextFile(tFile, 8).Line 'ファイルの行数取得

➁では➀で取得した「tFile」を使っていますが、
➁では、フルパスを指定すべきところ、ファイル名だけになっています。
「tFile」は、別のところでも使っているので、確認が必要です。
「tFile」で、動いているとすれば、@は間違いということになりませんか?
 
問題点2

引用:

        Do Until .EOS

.EOS
は、間違いですよね?
 
問題点3

引用:

                    tmp = Split(Replace(replaceColon(strLine), """", ""), ":")

このコードは、何をしているのでしょうか?
 
問題4
D
引用:

                    If n > UBound(ary, 1) Then
                                       
                        ReDim Preserve ary(0 To n, 0 To max_items) As Variant ' 配列の再設定
                           
                    End If

このコードは、不要と思います。
max_nはファイルの中のデータ件数のはずです。
しかし、aryの一次元は「0」から始まっているので、実際のデータより1件多い。
もし、これが実行されると、実際にはエラーになります。
「n」を変更しているところを探すべきです。
掲示のコード(手入力による)が実際のコードと食い違いあれば、別の話です。

 
@・2の部分はご指摘下さった内容がよく理解できませんでした。
 
Bについては、ADODB.Streamで記述する場合にこのような記述になるとおもって記述を変更した記憶があります。
 
4については、確か数字の例えば2,000円とかがあると、途中のカンマで配列がが区切られてしまうのを防止するため、replaceColon関数を別でつくり、上手く配列を取り込むための処理をしている部分です。
 
Dについては、ご指摘下さったとおり不要かもしれませんが、何度やってもエラーが出たため、もしかしたらと思って念のため記述したコードになります。
 
いろいろとご検討及ぼご指摘下さり、有難うございます!今からいろいろ試して仕上げたいと思っております。