┏第65号━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃         .NETプログラミング研究         ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ──<メニュー>─────────────────────── ■.NET質問箱 ・CSV形式のファイルをDataTableや配列等として取得する ・DataTableや配列等をCSV形式のファイルとして保存する ─────────────────────────────── ─────────────────────────────── ■.NET質問箱 ─────────────────────────────── 「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込 まれた.NETプログラミングに関する投稿を基に、さらに考察を加え、 Q&A形式にまとめて紹介します。 [URL]どぼん!のプログラミング掲示板 http://dobon.net/vb/bbs.html ─────────────────────────────── ●CSV形式のファイルをDataTableや配列等として取得する 【質問】 CSV(Comma Separated Value)形式のファイルをDataTableや配列と して読み込むことはできませんか? 【回答】 これには幾つかの方法が考えられます。ここでは3つの方法を紹介し ますが、その前にCSVとはなにかについて確認しておきます。 CSV形式について、絶対的な決まりは存在していないようです。ただ し、一般的なアプリケーションで使われている決まりについては、次 のサイトで説明されています。 [URL]CSV Comma Separated Value File Format (How To) http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm 要約しますと、次のようになります。 1.レコードは、LFまたはCRLFで区切られる。 2.フィールドは、カンマ(,)で区切られる。 3.区切りのカンマの前後のスペース(タブを含む)は無視される。 4.フィールドにカンマが含まれる場合、フィールドをダブルクォート で囲まなければならない。 5.フィールドにダブルクォート(")が含まれる場合、フィールドを ダブルクォートで囲み、フィールド内のダブルクォートを2つの連続 するダブルクォート(つまり、「""」)に置き換えなければならない。 6.フィールドが改行文字を含む場合、フィールドをダブルクォートで 囲まなければならない。 7.フィールドの前後にスペースがある場合、フィールドをダブルクォー トで囲まなければならない。 8.すべてのフィールドがダブルクォートで囲まれているかもしれない。 9.はじめのレコードは、ヘッダかもしれない。 ここでは、ここのような決まりに準じて、話を進めることにします。 CSV形式のファイルを配列として取得する方法としてよく使われるの が、CSVファイルから一行ずつ読み出し、その一行をString.Splitメ ソッドを使って「,」文字で分割するという方法です。しかしこの方 法は、上記の規則の4,6を見れば、正しくないことが分かります。つ まり、フィールドにカンマや改行文字が含まれている可能性があるの です。よってこのような方法は、フィールドにカンマや改行文字が含 まれていないことが保障されたCSVにしか使用できません。そうでな いCSVを扱うためには、より複雑な処理が必要になります。 さて、ここから本題に入ります。まずは、Jet ProviderやODBC Providerを使う方法を紹介します。これらを使って、CSVファイルを 解析することができます。 以下にJet Providerを使った例を紹介します。ここでは解析された CSVファイルの内容をDataTableに格納しています。CSVファイルの文 字コードは、Shift JISである必要があります。なお、接続文字列の 「HDR=No」を「HDR=Yes」とすることにより、一行目をヘッダとする ことができます。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 'CSVファイルのあるフォルダ Dim csvDir As String = "C:\" 'CSVファイルの名前 Dim csvFileName As String = "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) ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //CSVファイルのあるフォルダ string csvDir = @"C:\"; //CSVファイルの名前 string csvFileName = "test.csv"; //接続文字列 string conString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + csvDir + ";Extended Properties=\"text;HDR=No;FMT=Delimited\""; System.Data.OleDb.OleDbConnection con = new System.Data.OleDb.OleDbConnection(conString); string commText = "SELECT * FROM [" + csvFileName + "]"; System.Data.OleDb.OleDbDataAdapter da = new System.Data.OleDb.OleDbDataAdapter(commText, con); //DataTableに格納する DataTable dt = new DataTable(); da.Fill(dt); ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 次はODBC Provider(Microsoft Text Driver)を使った例です。.NET Framework 1.1以降で使用できます。なおこの場合は、一行目がヘッ ダとして処理されます。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 'CSVファイルのあるフォルダ Dim csvDir As String = "C:\" 'CSVファイルの名前 Dim csvFileName As String = "test.csv" '接続文字列 Dim conString As String = _ "Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=" _ + csvDir + ";Extensions=asc,csv,tab,txt;" Dim con As New System.Data.Odbc.OdbcConnection(conString) Dim commText As String = "SELECT * FROM [" + csvFileName + "]" Dim da As New System.Data.Odbc.OdbcDataAdapter(commText, con) 'DataTableに格納する Dim dt As New DataTable da.Fill(dt) ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //CSVファイルのあるフォルダ string csvDir = @"C:\"; //CSVファイルの名前 string csvFileName = "test.csv"; //接続文字列 string conString = "Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=" + csvDir + ";Extensions=asc,csv,tab,txt;"; System.Data.Odbc.OdbcConnection con = new System.Data.Odbc.OdbcConnection(conString); string commText = "SELECT * FROM [" + csvFileName + "]"; System.Data.Odbc.OdbcDataAdapter da = new System.Data.Odbc.OdbcDataAdapter(commText, con); //DataTableに格納する DataTable dt = new DataTable(); da.Fill(dt); ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ このような方法では、正しくCSVファイルが解析されるようにするに は、Schema.iniを用意しておく必要があります。そうしないと、正し く解釈されない可能性が十分にあります。 [URL]Schema.ini File (Text File Driver) http://msdn.microsoft.com/library/en-us/odbc/htm/odbcjetschema_ini_file.asp Schema.iniファイルの作成法については、次のサイトが参考になりま す。 Importing CSV Data and saving it in database http://www.codeproject.com/cs/database/FinalCSVReader.asp [AC97]VBAから Schema.ini ファイルを作成する方法 http://support.microsoft.com/kb/155512/ja 参考: [URL]ConnectionStrings.com http://www.connectionstrings.com/ 次に正規表現を使った方法を紹介します。「Perlメモ」の「CSV形式 の行から値のリストを取り出す」や「値に改行コードを含む CSV形式 を扱う」が参考になります。 [URL]Perlメモ http://www.din.or.jp/~ohzaki/perl.htm これによると、CSVの一つの行(一つのレコード)からフィールドを 取り出すには、レコードの最後にカンマをつけてから、 ("(?:[^"]|"")*"|[^,]*), というパターンを使用します。 また、フィールドに改行コードを含む場合にCSV形式の文字列から一 つのレコードを取り出すには、まず一行を取り出してから、その文字 列内の「"」の数を数え、奇数であれば次の一行を追加します。これ を「"」の数が偶数になるまで繰り返し、偶数になったところで、一 つのレコードが取り出せたものとします。 フィールドを取り出すためのパターンは、他にもいろいろ考えられま す。例えば、一つのレコードからフィールドを取り出すためのパター ンとしては、 ,(?=([^\"]*"[^"]*")*(?![^"]*")) や (?:^|,)(\"(?:[^\"]+|\"\")*\"|[^,]*) などが以下のサイトで紹介されています。 [URL]CSV Regex Pattern http://blogs.worldnomads.com.au/matthewb/archive/2004/01/18/197.aspx [URL]RegEx for CSV http://geekswithblogs.net/mwatson/archive/2004/09/04/10658.aspx 「Perlメモ」による方法を参考にしたサンプルを以下に紹介します。 ここでは、先のCSV規則の3が適切に処理されるように、「Perlメモ」 のパターンに手を加えています。また、一行取り出すためにも、正規 表現を使っています。取り出したフィールドは、レコードごとに ArrayListに格納し、これらをさらにArrayListに格納しています(実 用的ではありませんが、あくまでCSVの解析のサンプルということで、 ご理解ください)。CSVにはヘッダが無く、すべてのフィールドを文 字列として取得します。CSV形式のファイルを解析する場合は、その 内容をString型に読み込んでからメソッドを呼び出してください。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ '/ '/ CSVをArrayListに変換 '/ '/ CSVの内容が入ったString '/ 変換結果のArrayList Public Shared Function CsvToArrayList1(ByVal csvText As String) _ As System.Collections.ArrayList Dim csvRecords As New System.Collections.ArrayList '前後の改行を削除しておく csvText = csvText.Trim( _ New Char() {ControlChars.Cr, ControlChars.Lf}) '一行取り出すための正規表現 Dim regLine As New System.Text.RegularExpressions.Regex( _ "^.*(?:\n|$)", _ System.Text.RegularExpressions.RegexOptions.Multiline) '1行のCSVから各フィールドを取得するための正規表現 Dim regCsv As New System.Text.RegularExpressions.Regex( _ "\s*(""(?:[^""]|"""")*""|[^,]*)\s*,", _ System.Text.RegularExpressions.RegexOptions.None) Dim mLine As System.Text.RegularExpressions.Match = _ regLine.Match(csvText) While mLine.Success '一行取り出す Dim line As String = mLine.Value '改行記号が"で囲まれているか調べる While CountString(line, """") Mod 2 = 1 mLine = mLine.NextMatch() If Not mLine.Success Then Throw New ApplicationException("不正なCSV") End If line += mLine.Value End While '行の最後の改行記号を削除 line = line.TrimEnd( _ New Char() {ControlChars.Cr, ControlChars.Lf}) '最後に「,」をつける line += "," '1つの行からフィールドを取り出す Dim csvFields As New System.Collections.ArrayList Dim m As System.Text.RegularExpressions.Match = _ regCsv.Match(line) While m.Success Dim field As String = m.Groups(1).Value '前後の空白を削除 field = field.Trim() '"で囲まれている時 If field.StartsWith("""") And field.EndsWith("""") Then '前後の"を取る field = field.Substring(1, field.Length - 2) '「""」を「"」にする field = field.Replace("""""", """") End If csvFields.Add(field) m = m.NextMatch() End While csvFields.TrimToSize() csvRecords.Add(csvFields) mLine = mLine.NextMatch() End While csvRecords.TrimToSize() Return csvRecords End Function '/ '/ 指定された文字列内にある文字列が幾つあるか数える '/ '/ strFindが幾つあるか数える文字列 '/ 数える文字列 '/ strInput内にstrFindが幾つあったか Public Shared Function CountString( _ ByVal strInput As String, _ ByVal strFind As String) As Integer Dim foundCount As Integer = 0 Dim sPos As Integer = strInput.IndexOf(strFind) While sPos > -1 foundCount += 1 sPos = strInput.IndexOf(strFind, sPos + 1) End While Return foundCount End Function ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ /// /// CSVをArrayListに変換 /// /// CSVの内容が入ったString /// 変換結果のArrayList public static System.Collections.ArrayList CsvToArrayList1(string csvText) { System.Collections.ArrayList csvRecords = new System.Collections.ArrayList(); //前後の改行を削除しておく csvText = csvText.Trim(new char[] {'\r', '\n'}); //一行取り出すための正規表現 System.Text.RegularExpressions.Regex regLine = new System.Text.RegularExpressions.Regex( "^.*(?:\\n|$)", System.Text.RegularExpressions.RegexOptions.Multiline); //1行のCSVから各フィールドを取得するための正規表現 System.Text.RegularExpressions.Regex regCsv = new System.Text.RegularExpressions.Regex( "\\s*(\"(?:[^\"]|\"\")*\"|[^,]*)\\s*,", System.Text.RegularExpressions.RegexOptions.None); System.Text.RegularExpressions.Match mLine = regLine.Match(csvText); while (mLine.Success) { //一行取り出す string line = mLine.Value; //改行記号が"で囲まれているか調べる while ((CountString(line, "\"") % 2) == 1) { mLine = mLine.NextMatch(); if (!mLine.Success) { throw new ApplicationException("不正なCSV"); } line += mLine.Value; } //行の最後の改行記号を削除 line = line.TrimEnd(new char[] {'\r', '\n'}); //最後に「,」をつける line += ","; //1つの行からフィールドを取り出す System.Collections.ArrayList csvFields = new System.Collections.ArrayList(); System.Text.RegularExpressions.Match m = regCsv.Match(line); while (m.Success) { string field = m.Groups[1].Value; //前後の空白を削除 field = field.Trim(); //"で囲まれている時 if (field.StartsWith("\"") && field.EndsWith("\"")) { //前後の"を取る field = field.Substring(1, field.Length - 2); //「""」を「"」にする field = field.Replace("\"\"", "\""); } csvFields.Add(field); m = m.NextMatch(); } csvFields.TrimToSize(); csvRecords.Add(csvFields); mLine = mLine.NextMatch(); } csvRecords.TrimToSize(); return csvRecords; } /// /// 指定された文字列内にある文字列が幾つあるか数える /// /// strFindが幾つあるか数える文字列 /// 数える文字列 /// strInput内にstrFindが幾つあったか public static int CountString(string strInput, string strFind) { int foundCount = 0; int sPos = strInput.IndexOf(strFind); while (sPos > -1) { foundCount++; sPos = strInput.IndexOf(strFind, sPos + 1); } return foundCount; } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 最後に紹介するのは、文字列を独自に解析する方法です。面倒ですが、 これが一番速いかもしれません。 以下にその例を示します。使い方は先のCsvToArrayList1メソッドと 同じです。 (テストが十分でないため、間違いがあるかもしれません。不具合を 発見された方は、ぜひご報告ください。) ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ '/ '/ CSVをArrayListに変換 '/ '/ CSVの内容が入ったString '/ 変換結果のArrayList Public Shared Function CsvToArrayList2(ByVal csvText As String) _ As System.Collections.ArrayList '前後の改行を削除しておく csvText = csvText.Trim( _ New Char() {ControlChars.Cr, ControlChars.Lf}) Dim csvRecords As New System.Collections.ArrayList Dim csvFields As New System.Collections.ArrayList Dim csvTextLength As Integer = csvText.Length Dim startPos As Integer = 0 Dim endPos As Integer = 0 Dim field As String = "" While True '空白を飛ばす While startPos < csvTextLength _ AndAlso (csvText.Chars(startPos) = " "c _ OrElse csvText.Chars(startPos) = ControlChars.Tab) startPos += 1 End While 'データの最後の位置を取得 If startPos < csvTextLength _ AndAlso csvText.Chars(startPos) = ControlChars.Quote Then '"で囲まれているとき '最後の"を探す endPos = startPos While True endPos = csvText.IndexOf(ControlChars.Quote, endPos + 1) If endPos < 0 Then Throw New ApplicationException("""が不正") End If '"が2つ続かない時は終了 If endPos + 1 = csvTextLength OrElse _ csvText.Chars((endPos + 1)) <> ControlChars.Quote Then Exit While End If '"が2つ続く endPos += 1 End While '一つのフィールドを取り出す field = csvText.Substring(startPos, endPos - startPos + 1) '""を"にする field = field.Substring(1, field.Length - 2). _ Replace("""""", """") endPos += 1 '空白を飛ばす While endPos < csvTextLength AndAlso _ csvText.Chars(endPos) <> ","c AndAlso _ csvText.Chars(endPos) <> ControlChars.Lf endPos += 1 End While Else '"で囲まれていない 'カンマか改行の位置 endPos = startPos While endPos < csvTextLength AndAlso _ csvText.Chars(endPos) <> ","c AndAlso _ csvText.Chars(endPos) <> ControlChars.Lf endPos += 1 End While '一つのフィールドを取り出す field = csvText.Substring(startPos, endPos - startPos) '後の空白を削除 field = field.TrimEnd() End If 'フィールドの追加 csvFields.Add(field) '行の終了か調べる If endPos >= csvTextLength OrElse _ csvText.Chars(endPos) = ControlChars.Lf Then '行の終了 'レコードの追加 csvFields.TrimToSize() csvRecords.Add(csvFields) csvFields = New System.Collections.ArrayList( _ csvFields.Count) If endPos >= csvTextLength Then '終了 Exit While End If End If '次のデータの開始位置 startPos = endPos + 1 End While csvRecords.TrimToSize() Return csvRecords End Function ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ /// /// CSVをArrayListに変換 /// /// CSVの内容が入ったString /// 変換結果のArrayList public static System.Collections.ArrayList CsvToArrayList2(string csvText) { //前後の改行を削除しておく csvText = csvText.Trim(new char[] {'\r', '\n'}); System.Collections.ArrayList csvRecords = new System.Collections.ArrayList(); System.Collections.ArrayList csvFields = new System.Collections.ArrayList(); int csvTextLength = csvText.Length; int startPos = 0, endPos = 0; string field = ""; while (true) { //空白を飛ばす while (startPos < csvTextLength && (csvText[startPos] == ' ' || csvText[startPos] == '\t')) { startPos++; } //データの最後の位置を取得 if (startPos < csvTextLength && csvText[startPos] == '"') { //"で囲まれているとき //最後の"を探す endPos = startPos; while (true) { endPos = csvText.IndexOf('"', endPos + 1); if (endPos < 0) { throw new ApplicationException("\"が不正"); } //"が2つ続かない時は終了 if (endPos + 1 == csvTextLength || csvText[endPos + 1] != '"') { break; } //"が2つ続く endPos++; } //一つのフィールドを取り出す field = csvText.Substring(startPos, endPos - startPos + 1); //""を"にする field = field.Substring(1, field.Length - 2).Replace("\"\"", "\""); endPos++; //空白を飛ばす while (endPos < csvTextLength && csvText[endPos] != ',' && csvText[endPos] != '\n') { endPos++; } } else { //"で囲まれていない //カンマか改行の位置 endPos = startPos; while (endPos < csvTextLength && csvText[endPos] != ',' && csvText[endPos] != '\n') { endPos++; } //一つのフィールドを取り出す field = csvText.Substring(startPos, endPos - startPos); //後の空白を削除 field = field.TrimEnd(); } //フィールドの追加 csvFields.Add(field); //行の終了か調べる if (endPos >= csvTextLength || csvText[endPos] == '\n') { //行の終了 //レコードの追加 csvFields.TrimToSize(); csvRecords.Add(csvFields); csvFields = new System.Collections.ArrayList( csvFields.Count); if (endPos >= csvTextLength) { //終了 break; } } //次のデータの開始位置 startPos = endPos + 1; } csvRecords.TrimToSize(); return csvRecords; } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ このようなコードを自分で書かなくても、すでに優秀なクラスが多く 存在します。最後に、その内幾つかを以下に紹介しておきます。(上 の2つが代表的なものです。) [URL]A Fast CSV Reader http://www.codeproject.com/cs/database/CsvReader.asp [URL]XmlCsvReader Implementation http://msdn.microsoft.com/library/en-us/dnxmlnet/html/xmlcsvreader.asp [URL]A portable and efficient generic parser for flat files http://www.codeproject.com/cs/database/GenericParser.asp [URL]ASC2XXX - Two classes for parsing delimited text files (正規表現を使用しているようです) http://www.codeproject.com/cs/database/asc2xxx.asp ○この記事の基になった掲示板のスレッド [題名] CSVファイルの総行数を取得したいです。 [投稿者(敬称略)] こつ, 小野@どっとねっとふぁん, こど。, 壱丸3, じゃんぬねっと [URL] http://dobon.net/vb/bbs/log3-16/9785.html [題名] CSV箕荷 [投稿者(敬称略)] kenta, 岡田 之仁, sas [URL] http://dobon.net/vb/bbs/log3-7/4050.html [題名] CSVファイルをデータグリットで表示 [投稿者(敬称略)] A, nepia [URL] http://dobon.net/vb/bbs/log3-8/4876.html [題名] CSVファイルについて [投稿者(敬称略)] Boo, 岡田 之仁 [URL] http://dobon.net/vb/bbs/log3-12/6599.html [題名] CSVデータをODBCで削除するには? [投稿者(敬称略)] おすぎ, 深山 [URL] http://dobon.net/vb/bbs/log3-13/7393.html [題名] テキストファイルを実行ファイルに取り込んで扱う方法 [投稿者(敬称略)] だまさい, Mike [URL] http://dobon.net/vb/bbs/log3-3/1430.html [題名] ArrayListの使い方 [投稿者(敬称略)] kyoro, Blue, 中博俊 [URL] http://dobon.net/vb/bbs/log3-18/10598.html [題名] 多次元配列(二次元配列)の初期化について [投稿者(敬称略)] HOGE, java.lang.Nullpo [URL] http://dobon.net/vb/bbs/log3-9/5298.html [題名] Split [投稿者(敬称略)] SOMY, まどか, なおこ(・∀・) [URL] http://dobon.net/vb/bbs/log3-21/13151.html ─────────────────────────────── ●DataTableや配列等をCSV形式のファイルとして保存する 【質問】 DataTableや配列をCSV形式のファイルとして保存するにはどのように すればよいでしょうか? 【解答】 ここでもCSV形式の規則は、先ほど紹介したものと同じとして、話を 進めます。つまり、4,6の規則に従い、フィールドにカンマ、改行文 字が含まれる場合は、ダブルクォートで囲みます。また5の規則に従 い、フィールドにダブルクォートが含まれる場合は、これを2つのダ ブルクォートに置換して、ダブルクォートで囲みます。さらに規則7 に従い、フィールドの前後にスペースがある場合も、ダブルクォート で囲みます。 このような方針により、DataTableをCSV形式のファイルに保存する例 を以下に示します。フィールドの型を考慮せず、単純にToStringメソ ッドで文字列にして保存しています。ヘッダも書き込んでいますが、 ヘッダの書き込みと、レコードの書き込みが全く同じコードになって おり、コードとしてはあまり良くありません。あくまでサンプルとい うことで、ご了承ください。 またこの例では、いちいち一つ一つのフィールドを調べてダブルクォー トで囲むか調べていますが、これが面倒であれば、すべてのフィール ドをダブルクォートで囲むようにしても結構です。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 'CSVで保存するDataTable Dim dt As DataTable = CType(DataGrid1.DataSource, DataTable) '保存先のCSVファイルのパス Dim csvPath As String = "C:\test1.csv" 'CSVファイルに書き込むときに使うEncoding Dim enc As System.Text.Encoding = _ System.Text.Encoding.GetEncoding("Shift_JIS") '開く Dim sr As New System.IO.StreamWriter(csvPath, False, enc) Dim colCount As Integer = dt.Columns.Count Dim lastColIndex As Integer = colCount - 1 'ヘッダを書き込む Dim i As Integer For i = 0 To colCount - 1 'ヘッダの取得 Dim field As String = dt.Columns(i).Caption '"で囲む必要があるか調べる If field.IndexOf(ControlChars.Quote) > -1 OrElse _ field.IndexOf(","c) > -1 OrElse _ field.IndexOf(ControlChars.Cr) > -1 OrElse _ field.IndexOf(ControlChars.Lf) > -1 OrElse _ field.StartsWith(" ") OrElse _ field.StartsWith(ControlChars.Tab) OrElse _ field.EndsWith(" ") OrElse _ field.EndsWith(ControlChars.Tab) Then If field.IndexOf(ControlChars.Quote) > -1 Then '"を""とする field = field.Replace("""", """""") End If field = """" + field + """" End If 'フィールドを書き込む sr.Write(field) 'カンマを書き込む If lastColIndex > i Then sr.Write(","c) End If Next i '改行する sr.Write(ControlChars.Cr + ControlChars.Lf) 'レコードを書き込む Dim row As DataRow For Each row In dt.Rows For i = 0 To colCount - 1 'フィールドの取得 Dim field As String = row(i).ToString() '"で囲む必要があるか調べる If field.IndexOf(ControlChars.Quote) > -1 OrElse _ field.IndexOf(","c) > -1 OrElse _ field.IndexOf(ControlChars.Cr) > -1 OrElse _ field.IndexOf(ControlChars.Lf) > -1 OrElse _ field.StartsWith(" ") OrElse _ field.StartsWith(ControlChars.Tab) OrElse _ field.EndsWith(" ") OrElse _ field.EndsWith(ControlChars.Tab) Then If field.IndexOf(ControlChars.Quote) > -1 Then '"を""とする field = field.Replace("""", """""") End If field = """" + field + """" End If 'フィールドを書き込む sr.Write(field) 'カンマを書き込む If lastColIndex > i Then sr.Write(","c) End If Next i '改行する sr.Write(ControlChars.Cr + ControlChars.Lf) Next row '閉じる sr.Close() ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //CSVで保存するDataTable DataTable dt = (DataTable) dataGrid1.DataSource; //保存先のCSVファイルのパス string csvPath = "C:\\test1.csv"; //CSVファイルに書き込むときに使うEncoding System.Text.Encoding enc = System.Text.Encoding.GetEncoding("Shift_JIS"); //開く System.IO.StreamWriter sr = new System.IO.StreamWriter(csvPath, false, enc); int colCount = dt.Columns.Count; int lastColIndex = colCount - 1; //ヘッダを書き込む for (int i = 0; i < colCount; i++) { //ヘッダの取得 string field = dt.Columns[i].Caption; //"で囲む必要があるか調べる if (field.IndexOf('"') > -1 || field.IndexOf(',') > -1 || field.IndexOf('\r') > -1 || field.IndexOf('\n') > -1 || field.StartsWith(" ") || field.StartsWith("\t") || field.EndsWith(" ") || field.EndsWith("\t")) { if (field.IndexOf('"') > -1) { //"を""とする field = field.Replace("\"", "\"\""); } field = "\"" + field + "\""; } //フィールドを書き込む sr.Write(field); //カンマを書き込む if (lastColIndex > i) { sr.Write(','); } } //改行する sr.Write("\r\n"); //レコードを書き込む foreach (DataRow row in dt.Rows) { for (int i = 0; i < colCount; i++) { //フィールドの取得 string field = row[i].ToString(); //"で囲む必要があるか調べる if (field.IndexOf('"') > -1 || field.IndexOf(',') > -1 || field.IndexOf('\r') > -1 || field.IndexOf('\n') > -1 || field.StartsWith(" ") || field.StartsWith("\t") || field.EndsWith(" ") || field.EndsWith("\t")) { if (field.IndexOf('"') > -1) { //"を""とする field = field.Replace("\"", "\"\""); } field = "\"" + field + "\""; } //フィールドを書き込む sr.Write(field); //カンマを書き込む if (lastColIndex > i) { sr.Write(','); } } //改行する sr.Write("\r\n"); } //閉じる sr.Close(); ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ このような方法以外に、掲示板では、XSLTを使ってCSVに変換する方 法が紹介されています。この方法については、記事の最後にある掲示 板の過去ログへのリンク(「DatasetのデータをCSV保存する」)や、 次のリンク先をご覧ください。 [URL]XSLT to transform Excel XML spreadsheet to CSV or HTML table http://www.codeproject.com/soap/xml_spreadsheet_to_csv.asp [URL]XSLT to Convert Dataset XML to CSV http://groups.google.co.jp/group/microsoft.public.dotnet.framework.adonet/msg/8fa7f8b03000fe26?hl=ja& 私個人の意見としましては、このような方法では上記に示したCSVの 規則に従うファイルを出力するのが難しく、柔軟性にも乏しいため、 お勧めはできません。 ○この記事の基になった掲示板のスレッド [題名] csvファイルの読み書き [投稿者(敬称略)] KIRIRI, なおこ(・∀・), trapemiya, じゃんぬね っと [URL] http://dobon.net/vb/bbs/log3-21/12803.html [題名] DatasetのデータをCSV保存する [投稿者(敬称略)] ほげほげ, 中 博俊, えムナウ [URL] http://dobon.net/vb/bbs/log3-14/8300.html [題名] ADO.NETからの出力結果のカラム名を取り出すには? [投稿者(敬称略)] あみっど [URL] http://dobon.net/vb/bbs/log2/780.html =============================== ■ここで示したコードの多くはまずC#で書き、それを「C# to VB.NET Translator」でVB.NETのコードに変換し、修正を加えたものです。 [URL]C# to VB.NET Translator http://authors.aspalliance.com/aldotnet/examples/translate.aspx ■このマガジンの購読、購読中止、バックナンバー、説明に関しては  次のページをご覧ください。  http://www.mag2.com/m/0000104516.htm ■発行人・編集人:どぼん!  (Microsoft MVP for Visual Basic, Oct 2005-Oct 2006)  http://dobon.net  dobon_info@yahoo.co.jp ■ご質問等はメールではなく、掲示板へお願いいたします。  http://dobon.net/vb/bbs.html ■上記メールアドレスへのメールは確実に読まれる保障はありません  (スパム、ウィルス対策です)。メールは下記URLのフォームメール  から送信してください。  http://dobon.net/mail.html Copyright (c) 2003 - 2006 DOBON! All rights reserved. ===============================