DOBON.NET DOBON.NETプログラミング掲示板過去ログ

CSVファイルを相対パスでOLEDB接続し、DataTableに格納する際のエラー

環境/言語:[OS : Windows XP Home Edition / 言語 : Visual Basic .NET]
分類:[.NET]

【解決したい問題】

はじめまして。
CSVファイル(ヘッダーなし)をDataGridのデータソースに設定したいと思い、
GooGleで検索した所、こちらのサイトを拝見しました。
http://dobon.net/vb/dotnet/file/readcsvfile.html

ご提示いただいているコードをそのまま使用させて頂いた時には
思い通りにDataGirdへ表示することができたのですが、
フォルダとファイルの名前をダイアログで取得したもの(相対パス)
を変数に代入し、実行したところ、da.Fill(dt) でエラーになって
しまいました。

---「エラー内容」---------------------------------
'System.Data.OleDb.OleDbException' のハンドルされて
いない例外が system.data.dll で発生しました。
--------------------------------------------------

---「参考サイトを※印のように修正しました」-------

'[VB.NET]
Dim csvAllFileName As String = Me.lblImpName.Text '※
                    '↑"C:\test.csv" が入っています

'CSVファイルのあるフォルダ
'Dim csvDir As String = "C:\"
Dim csvDir = System.IO.Path.GetDirectoryName(csvAllFileName) '※
   '↑"C:\"が入っていました

'CSVファイルの名前
'Dim csvFileName As String = "test.csv"
Dim csvFileName As String = System.IO.Path.GetFileName(csvAllFileName) '※
   '↑"test.csv"が入っていました

'接続文字列
Dim conString As String = _
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" _
+ csvDir + ";Extended Properties=""text;HDR=No;FMT=Delimited"""
Dim con As New System.Data.OleDb.OleDbConnection(conString)

Dim commText As String = "SELECT * FROM [" + csvFileName + "]"
Dim da As New System.Data.OleDb.OleDbDataAdapter(commText, con)

'DataTableに格納する
Dim dt As New DataTable
da.Fill(dt)   '←ここでエラーになります
Me.dgrTest.DataSource = dt '※
--------------------------------------------------------

---「エラーを中断させた時の変数の中身」-----------------
csvAllFileName → "C:\test.csv"
csvDir → "C:\"
csvFileName → "test.csv"
conString → "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=
       C:\;Extended Properties="text;HDR=No;FMT=Delimited"
commText → "SELECT * FROM [test.csv]"
--------------------------------------------------------

以上です。
ご提示頂いているものをそのまま使用させて頂くと正しく表示できるのに、
相対パスにするとエラーになってしまい、原因がわからず困っています。

どうぞ、よろしくお願いします。
お世話になります。すももです。

ディレクトリ名 csvDir に Application.StartupPath を設定すると、
正常に起動してくれました。

なので、任意に取得したファイルを Application.StartupPathフォルダへ
コピーし、コピー時に作成したファイルで実行したところ、思い通りの事が
できました。

---「以下、修正コード」※※部分が修正した箇所です ----------------
Dim csvAllFileName As String = Me.lblImpName.Text '※
                    '↑"C:\test.csv" が入っています
                    '深い階層のファイル名でもOKでした。

'CSVファイルのあるフォルダ
'Dim csvDir As String = "C:\"
'Dim csvDir = System.IO.Path.GetDirectoryName(csvAllFileName) '※
   '↑"C:\"が入っていました
Dim csvDir As String = Application.StartupPath         '※※

'ディレクトリ名の最後が"\"でない場合、後ろに"\"をつける処理   '※※
If csvDir.Substring(Len(csvDir) - 1) <> "\" Then csvDir += "\" '※※

'ファイルをコピーする。(同名があれば上書)
System.IO.File.Copy(csvAllFileName, csvDir & "oledbdatatb.csv", True) '※※

'CSVファイルの名前
'Dim csvFileName As String = "test.csv"
'Dim csvFileName As String = System.IO.Path.GetFileName(csvAllFileName) '※
   '↑"test.csv"が入っていました
Dim csvFileName As String = "oledbdatatb.csv"   '※※

'<< 以下同じ >>
'接続文字列
Dim conString As String = _
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" _
+ csvDir + ";Extended Properties=""text;HDR=No;FMT=Delimited"""
Dim con As New System.Data.OleDb.OleDbConnection(conString)

Dim commText As String = "SELECT * FROM [" + csvFileName + "]"
Dim da As New System.Data.OleDb.OleDbDataAdapter(commText, con)

'DataTableに格納する
Dim dt As New DataTable
da.Fill(dt)   '←ここでエラーになります ※※解消されました
Me.dgrTest.DataSource = dt '※

-----------------------------------------------------------------


以上、※※に修正した結果、思い通りに動いてくれました。

こちら以外の参考にしたサイト
http://jeanne.wankuma.com/tips/
http://park5.wakwak.com/~weblab/mag019.html


上記で解決したのですが、他に方法があるなど、お気づきの点があれば、
ご意見お聞かせ頂けたら幸いです。

管理人様、
もう少しこのままにさせて頂き、コメントがつかないようでしたら、
解決済みとさせて頂きたいと思うのですが、よろしいでしょうか?

もし、この掲示板にふさわしくない行動でしたら、
申し訳ございませんが、コメントお願いします。

どうぞ、よろしくお願いします。
> 管理人様、
> もう少しこのままにさせて頂き、コメントがつかないようでしたら、
> 解決済みとさせて頂きたいと思うのですが、よろしいでしょうか?

解決済みについてのルールは特にありませんので、常識の範囲内であれば、結構です。
■No19855に返信(すももさんの記事)
ちゃんとデバッグしてることが分かるし、好感が持てるんだけど、
説明されている内容が良く分かりませんでした。

[相対パスとは]
http://e-words.jp/w/E79BB8E5AFBEE38391E382B9.html
どこにも出てきてない気がします。。。
提示されたコードは全て絶対パスで指定してますよね?

エラーが起こるとされているサンプルコードのパスですが。。。
DOBONさんのTipsと同じパスが代入されているだけですし。。。
どこに対象のファイルが存在しているのかが、ちょっと。。。

結論としては実行ファイル(EXE)の起動場所にファイルがないと
読み込めないと?。。。そんなことないと思いますが。。。うーん。
るしぇさん、コメントありがとうございます。

> [相対パスとは]
> http://e-words.jp/w/E79BB8E5AFBEE38391E382B9.html
> どこにも出てきてない気がします。。。
> 提示されたコードは全て絶対パスで指定してますよね?

相対パスの意味を、任意に取得した絶対パスと勘違いしていました。
ご指摘ありがとうございます。



> エラーが起こるとされているサンプルコードのパスですが。。。
> DOBONさんのTipsと同じパスが代入されているだけですし。。。
> どこに対象のファイルが存在しているのかが、ちょっと。。。

フォルダ名とファイル名の指定方法を
Dim csvDir As String = "C:\"
Dim csvFileName As String = "test.csv"
のように、固定するのではなく、ダイアログで選択した任意の
絶対パスからフォルダ名とファイル名を指定したいと思い、

Dim csvAllFileName As String = Me.lblImpName.Text
Dim csvDir as string = System.IO.Path.GetDirectoryName(csvAllFileName)
Dim csvFileName As String = System.IO.Path.GetFileName(csvAllFileName)
としたのですが、エラーになってしまったので、
DOBONさんのTipsと同じファイル名をテスト用に作成しました。

対象のファイルはC:\text.csvになります。


> 結論としては実行ファイル(EXE)の起動場所にファイルがないと
> 読み込めないと?。。。そんなことないと思いますが。。。うーん。

Dim csvDir as string = "C:\"
Dim csvFileName As String = "test.csv"
のように固定して実行すると、正常にデータが取得できるのですが、

Dim csvAllFileName As String = Me.lblImpName.Text
Dim csvDir = System.IO.Path.GetDirectoryName(csvAllFileName)
Dim csvFileName As String = System.IO.Path.GetFileName(csvAllFileName)
とすると、固定させているものと同じ内容が代入されているのに、
da.Fill(dt)でエラーになってしまいます。

ただ、csvDirとcsvFileNameの代入方法を上記のようにしているだけで、
csvDir、csvFileNameには同じ内容が代入されているのにもかかわらず、
固定させるとOK、任意に指定するとエラーとなってまうため、原因が
解明できず、仕方なく■19855 / inTopicNo.2)の方法を使用した次第です。


今回、るしぇさんのコメントを拝見し、
Dim csvAllFileName As String = Me.lblImpName.Text を
Dim csvAllFileName As String = "C:\test.csv"
にしてみたところ、正常に実行されました。

エラー原因は、csvAllFileNameへの代入の仕方が悪いのかと思い、
Dim csvAllFileName As String = CType(Me.lblImpName.Text,String)
としてみたのですが、エラーになってしまいました。
変数には "C:\" "test.csv" とそれぞれ固定値と同じものが
代入されていました。

エラーになるのだから、記述方法に問題があるかとは思うのですが、
何がいけないのか、原因がつかめません。

コメント頂けると幸いです。
よろしくお願いします。
■No19902に返信(すももさんの記事)
> Dim csvAllFileName As String = Me.lblImpName.Text を
> Dim csvAllFileName As String = "C:\test.csv"
> にしてみたところ、正常に実行されました。
不思議な現象ですね。Me.lblImpName.Text に問題があるとしか思えない。
ラベルってことは手入力では無いんですか?どこからどのようにして
取得していますか?末尾に NullChar とかゴミが付いてる可能性は?

あと、エラーメッセージは何が?

ちなみに、他のPCにインストールする状況になった場合、
> 任意に取得したファイルを Application.StartupPathフォルダへコピーし
では問題になることがあります。一般的にアプリケーションのインストール先
は、C:\Program Files の下になりますが、ここは管理者権限がないと書込み
できません。
一時ファイルが必要な場合は、ユーザごとの ApplicationData フォルダを使う
場合が多いかな?
[特殊ディレクトリのパスを取得する]
http://dobon.net/vb/dotnet/file/getfolderpath.html
…だから StartupPath でないと実行できないのは一般的にはマズいです。

> 変数には "C:\" "test.csv" とそれぞれ固定値と同じものが
> 代入されていました。
ほんとにぃ〜?ww
とりあえず、単純に比較してみてください。
[VB.NET2003]
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim csvDir_Default As String = "C:\"
        Dim csvFileName_Default As String = "test.csv"

        Dim csvAllFileName As String = Me.lblImpName.Text
        Dim csvDir As String = System.IO.Path.GetDirectoryName(csvAllFileName)
        Dim csvFileName As String = System.IO.Path.GetFileName(csvAllFileName)

        csvDir = "c:\  " 'test コメントアウトしてね。
        csvFileName = "test.csv" 'test コメントアウトしてね。

        Call StringCheck(csvDir, csvDir_Default)
        Call StringCheck(csvFileName, csvFileName_Default)

    End Sub

    Private Sub StringCheck(ByVal StrA As String, ByVal StrB As String)
        If StrA.CompareTo(StrB) <> 0 Then
            Debug.WriteLine("違うよ?")

            With StrA
                For i As Integer = 0 To .Length - 1
                    If StrB.Length - 1 < i Then
                        Debug.WriteLine("余った。[" & .Substring(i) & "]")
                        Exit Sub
                    End If

                    If .Substring(i, 1).CompareTo(StrB.Substring(i, 1)) <> 0 Then
                        Debug.WriteLine("--- [" & .Substring(i, 1) & "]≠[" & StrB.Substring(i, 1) & "]")
                    End If
                Next
            End With

        Else
            Debug.WriteLine("同じです。[" & StrA & "]")
        End If

    End Sub
2007/07/01(Sun) 00:42:59 編集(投稿者)

るしぇさん、ありがとうございます。
返事が遅くなってしまってすみません。。。

るしぇさんのご質問からヒントを得まして、問題が解決しました。
原因は、私が提示していたコードではなく、その前の段階の
csvファイル名を取得する部分にありました。

ダイアログでcsvファイルを指定した後、ファイル名をラベルに表示させる前に
strLine = objFile.ReadLine() でCSVファイルの一行目を取得し、
一行目のフィールドのみをDataGridに表示させる。
という部分があったのですが、一行目を取得する際にファイルが
開いた状態のまま閉じていなかったのが原因みたいです。

strLine = objFile.ReadLine() 'csvファイルの一行目を取得する
objFile.close  'csvファイルを閉じる

としたところ、正常に動いてくれました。


C:\、test.csvを固定にした時には、objFile.closeを入れなくても
正常に動いたので、ファイル名を取得する部分は問題がないと
思い込んでしまい、別ボタンの処理でもあったので、
コードを記載しませんでした。

早とちりをしてコードの全貌を記載せず、るしぇさんには、
無駄な時間を使わせてしまったかも知れません。
ファイル名が同じかどうか確認するコードまで記載して
頂いたのに、すみませんでした。。。


るしぇさんのご質問にて
> ラベルってことは手入力では無いんですか?どこからどのようにして
> 取得していますか?末尾に NullChar とかゴミが付いてる可能性は?
> あと、エラーメッセージは何が?

とありましたので、当初は
--------------------------------------------------
'System.Data.OleDb.OleDbException' のハンドルされて
いない例外が system.data.dll で発生しました。
--------------------------------------------------
が表示されていたのですが、エラー処理について、
Try Catchというものを知りましたので、
それを使用してエラーを出力してみた所、
-------------------------------------------------------------------------
System.Data.OleDb.OleDbException: ファイル '' を開くことができませんでした。
ほかのユーザーが排他的に開いているか、データを読み取る権限がありません。
-------------------------------------------------------------------------
とでました。
これがヒントになりました。


> ちなみに、他のPCにインストールする状況になった場合、
> StartupPath でないと実行できないのは一般的にはマズいです。
ご丁寧にありがとうございます。
とてもよく理解できました。
他PCで使用するときには、るしぇさんのご指摘のとおり、
アプリケーションをインストールする形になりますので、
教えて頂けてほんとに助かりました。


自己解決した時点で、解決済みにしなくてよかったです。
るしぇさん、管理人さん、ありがとうございました。
解決済み!
2007/07/02(Mon) 11:43:19 編集(投稿者)

■No19969に返信(すももさんの記事)
> 早とちりをしてコードの全貌を記載せず、るしぇさんには、
> 無駄な時間を使わせてしまったかも知れません。
いえいえ。デバッグの手順として正しいですよ?逆にそこまで
コードが載せてあったら回答を付けなかったかも(^^;
正常に実行できるものと、できないもので比較して、一番その
処理に関係している部分を抜き出し、調査し、質問する。
…かなりいいかんじですw。

原因を予想した部分に問題が無いことが確認できてから、更に他の
処理を調べるのが正しい手順でしょう。直接的な原因が質問部分に
含まれているかどうかも確証は無いはずです。だから質問している
はずですから(^^;
結果的に問題ない部分の動作確認に時間を使ったわけですが、これは
無駄な時間ではありません。すももさんは幾つかの予備知識を手に入れ
ましたし、ボクにとっても自分の知識の確認になりました。

今回の場合、すももさんの知識で予想した原因が間違っていましたが、
そここそが経験者が掲示板で助けるべきポイントだと思います。
質問掲示板の目的をかなり理想的な形で実現できたのではないでしょうか?
解決報告も理想的だと思います。ってゆーかこちらが嬉しくなる書込みですww。
そのままでいてくださいwww。
解決済み!
2007/07/03(Tue) 10:01:59 編集(投稿者)

るしぇさん、ご丁寧なコメントありがとうございます。
そう言っていただけて、私も嬉しい&安心しました^^

思い切って、ご質問させて頂いてよかったです。
今後とも、どうぞよろしくお願いいたします^^
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板