OSDN Git Service

CVSリポジトリから移行。CVSのコミットログは、移行が面倒なので消失。
[csv-io-net/repo.git] / csv-io-net / src / CsvIO / CsvWriter.cs
1 /*
2  * CSV I/O Library.NET
3  * Copyright (C) 2005, uguu All rights reserved.
4  * 
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 
9  *  - Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 
12  *  - Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 using System;
30 using System.Data;
31 using System.IO;
32
33 namespace Uguu.IO.Csv
34 {
35     /// <summary>
36     ///     <see cref="TextWriter"/> インスタンスに CSV 形式のデータを出力します。
37     /// </summary>
38     /// <remarks>
39     ///     インスタンスの作成時に指定した改行文字、区切り文字を使用して CSV データを出力します。
40     ///     出力する値に改行文字、区切り文字、ダブルクォーテーションが含まれる場合、エスケープして出力します。
41     ///     null 参照や <see cref="DBNull.Value"/> 値は空文字列として出力します。
42     /// </remarks>
43     public sealed class CsvWriter : IDisposable
44     {
45         /// <summary>
46         ///     出力先ライター。
47         /// </summary>
48         private TextWriter  writer;
49         /// <summary>
50         ///     改行文字。
51         /// </summary>
52         private string      newLine;
53         /// <summary>
54         ///     区切り文字。
55         /// </summary>
56         private string      delimiter;
57         /// <summary>
58         ///     出力列位置を表すインデックス番号。
59         /// </summary>
60         private int         columnIndex = 0;
61         /// <summary>
62         ///     出力行位置を表すインデックス番号。
63         /// </summary>
64         private int         rowIndex = 0;
65         /// <summary>
66         ///     指定した <see cref="TextWriter"/> インスタンスに CSV 形式のデータを出力する、
67         ///     <see cref="CsvWriter"/> クラスの新しいインスタンスを初期化します。
68         ///     改行文字は <see cref="Environment.NewLine"/> 、区切り文字は "," になります。
69         /// </summary>
70         /// <param name="writer">出力先ライター。</param>
71         /// <exception cref="ArgumentNullException"><paramref name="writer"/> 引数が null 参照の場合。</exception>
72         public CsvWriter(TextWriter writer) : this(writer, Environment.NewLine, ",")
73         {
74         }
75         /// <summary>
76         ///     指定した <see cref="TextWriter"/> インスタンスに指定した改行文字と区切り文字で CSV 形式のデータを出力する、
77         ///     <see cref="CsvWriter"/> クラスの新しいインスタンスを初期化します。
78         /// </summary>
79         /// <param name="writer">出力先ライター。</param>
80         /// <param name="newLine">改行文字。</param>
81         /// <param name="delimiter">区切り文字。</param>
82         /// <exception cref="ArgumentNullException">
83         ///     <paramref name="writer"/> 引数が null 参照の場合。
84         ///     <paramref name="newLine"/> 引数、 <paramref name="delimiter"/> 引数が null 参照か空文字列の場合。
85         /// </exception>
86         public CsvWriter(TextWriter writer, string newLine, string delimiter)
87         {
88             // 入力をチェックします。
89             if (writer == null)
90             {
91                 throw new ArgumentNullException("writer");
92             }
93             if (newLine == null || newLine == string.Empty)
94             {
95                 throw new ArgumentNullException("newLine");
96             }
97             if (delimiter == null || delimiter == string.Empty)
98             {
99                 throw new ArgumentNullException("delimiter");
100             }
101             // 初期化します。
102             this.writer = writer;
103             this.newLine = newLine;
104             this.delimiter = delimiter;
105         }
106         /// <summary>
107         ///     <see cref="CsvWriter"/> インスタンスを閉じます。
108         ///     出力先ライターも閉じます。
109         /// </summary>
110         public void Close()
111         {
112             if (this.writer != null)
113             {
114                 this.writer.Close();
115                 this.writer = null;
116             }
117         }
118         /// <summary>
119         ///     <see cref="CsvWriter"/> インスタンスを閉じます。
120         ///     出力先ライターも閉じます。
121         /// </summary>
122         public void Dispose()
123         {
124             if (this.writer != null)
125             {
126                 this.writer.Close();
127                 this.writer = null;
128             }
129         }
130         /// <summary>
131         ///     <see cref="CsvWriter"/> インスタンスが閉じているかどうかを取得します。
132         /// </summary>
133         public bool IsClosed
134         {
135             get
136             {
137                 return (this.writer == null);
138             }
139         }
140         /// <summary>
141         ///     現在の出力列位置を表すインデックス番号を取得します。
142         /// </summary>
143         /// <remarks>
144         ///     初期値直後の位置は 0 です。
145         /// </remarks>
146         public int ColumnIndex
147         {
148             get
149             {
150                 return this.columnIndex;
151             }
152         }
153         /// <summary>
154         ///     現在の出力行位置を表すインデックス番号を取得します。
155         /// </summary>
156         /// <remarks>
157         ///     初期値直後の位置は 0 です。
158         /// </remarks>
159         public int RowIndex
160         {
161             get
162             {
163                 return this.rowIndex;
164             }
165         }
166         /// <summary>
167         ///     単一の値を出力します。
168         /// </summary>
169         /// <param name="value">出力する値。</param>
170         /// <exception cref="ObjectDisposedException"><see cref="CsvWriter"/> インスタンスが既に閉じている場合。</exception>
171         public void Write(string value)
172         {
173             // インスタンスの状態をチェックします。
174             if (this.IsClosed)
175             {
176                 throw new ObjectDisposedException("writer");
177             }
178             
179             // 出力します。
180             string v = "";
181             if (value == null)
182             {
183                 value = string.Empty;
184             }
185             if (value.IndexOf(this.newLine) != -1 || value.IndexOf(this.delimiter) != -1)
186             {
187                 v += value.Replace("\"", "\"\"");
188                 v = "\"" + v + "\"";
189             }
190             else
191             {
192                 v += value;
193             }
194             if (this.columnIndex > 0)
195             {
196                 v = this.delimiter + v;
197             }
198             this.writer.Write(v);
199             this.columnIndex++;
200         }
201         /// <summary>
202         ///     改行します。
203         /// </summary>
204         /// <exception cref="ObjectDisposedException"><see cref="CsvWriter"/> インスタンスが既に閉じている場合。</exception>
205         public void WriteNewLine()
206         {
207             // インスタンスの状態をチェックします。
208             if (this.IsClosed)
209             {
210                 throw new ObjectDisposedException("writer");
211             }
212             
213             // 出力します。
214             this.writer.Write(this.newLine);
215             this.columnIndex = 0;
216             this.rowIndex++;
217         }
218         /// <summary>
219         ///     複数の値を連続して出力します。
220         /// </summary>
221         /// <param name="values">出力する値の配列。</param>
222         /// <exception cref="ArgumentNullException"><paramref name="values"/> 引数が null 参照の場合。</exception>
223         /// <exception cref="ObjectDisposedException"><see cref="CsvWriter"/> インスタンスが既に閉じている場合。</exception>
224         public void Write(string[] values)
225         {
226             // 入力をチェックします。
227             if (values == null)
228             {
229                 throw new ArgumentNullException("values");
230             }
231             // 出力します。
232             foreach (string value in values)
233             {
234                 this.Write(value);
235             }
236         }
237         /// <summary>
238         ///     複数の値を連続して出力します。
239         /// </summary>
240         /// <param name="values">出力する値の二次元配列。</param>
241         /// <exception cref="ArgumentNullException"><paramref name="values"/> 引数が null 参照の場合。</exception>
242         /// <exception cref="ObjectDisposedException"><see cref="CsvWriter"/> インスタンスが既に閉じている場合。</exception>
243         /// <remarks>
244         ///     <see cref="Write(string[])"/> メソッドは複数の値を連続して出力しますが、 <see cref="Write(string[][])"/> メソッドは複数行を出力します。
245         ///     出力は必ず新しい行から始まります。
246         ///     既に何らかの値が出力されている場合、改行してから出力を開始します。
247         ///     一行ごとに (当然ですが) 改行します。
248         ///     最後の行も改行します。
249         ///     したがって、全ての出力が完了すると、出力位置は新しい行の先頭になります。
250         /// </remarks>
251         public void Write(string[][] values)
252         {
253             // 入力をチェックします。
254             if (values == null)
255             {
256                 throw new ArgumentNullException("values");
257             }
258             foreach (string[] line in values)
259             {
260                 if (line == null)
261                 {
262                     throw new ArgumentNullException("values");
263                 }
264             }
265             // 出力します。
266             if (this.columnIndex > 0)
267             {
268                 this.WriteNewLine();
269             }
270             foreach (string[] line in values)
271             {
272                 foreach (string value in line)
273                 {
274                     this.Write(value);
275                 }
276                 this.WriteNewLine();
277             }
278         }
279         /// <summary>
280         ///     リーダーから値を読み込み、全て出力します。
281         /// </summary>
282         /// <param name="dataReader">値の読み込み元リーダー。</param>
283         /// <exception cref="ArgumentNullException"><paramref name="dataReader"/> 引数が null 参照の場合。</exception>
284         /// <exception cref="ObjectDisposedException"><see cref="CsvWriter"/> インスタンスが既に閉じている場合。</exception>
285         /// <remarks>
286         ///     <see cref="IDataReader"/> インスタンスから全ての列の値を取得し、全ての行を出力します。
287         ///     出力は必ず新しい行から始まります。
288         ///     既に何らかの値が出力されている場合、改行してから出力を開始します。
289         ///     一行ごとに (当然ですが) 改行します。
290         ///     最後の行も改行します。
291         ///     したがって、全ての出力が完了すると、出力位置は新しい行の先頭になります。
292         ///     全ての出力が完了しても <see cref="IDataReader"/> インスタンスは閉じられません。
293         /// </remarks>
294         public void Write(IDataReader dataReader)
295         {
296             this.Write(dataReader, null, int.MaxValue);
297         }
298         /// <summary>
299         ///     リーダーから値を読み込み、指定した列、及び行数を出力します。
300         /// </summary>
301         /// <param name="dataReader">値の読み込み元リーダー。</param>
302         /// <param name="names">
303         ///     出力する列名の配列。
304         ///     全ての列を出力する場合は null 参照を指定することができます。
305         /// </param>
306         /// <param name="count">出力する行数。</param>
307         /// <exception cref="ArgumentNullException"><paramref name="dataReader"/> 引数が null 参照の場合。</exception>
308         /// <exception cref="ArgumentException"><paramref name="count"/> 引数が 0 未満の場合。</exception>
309         /// <remarks>
310         ///     <see cref="IDataReader"/> インスタンスから指定した順番で列の値を取得し、指定した行数の行を出力します。
311         ///     出力は必ず新しい行から始まります。
312         ///     既に何らかの値が出力されている場合、改行してから出力を開始します。
313         ///     一行ごとに (当然ですが) 改行します。
314         ///     最後の行も改行します。
315         ///     したがって、全ての出力が完了すると、出力位置は新しい行の先頭になります。
316         ///     全ての出力が完了しても <see cref="IDataReader"/> インスタンスは閉じられません。
317         /// </remarks>
318         public void Write(IDataReader dataReader, string[] names, int count)
319         {
320             // 入力をチェックします。
321             if (dataReader == null)
322             {
323                 throw new ArgumentNullException("dataReader");
324             }
325             if (count < 0)
326             {
327                 throw new ArgumentException("count 引数が 0 未満です。");
328             }
329             // 出力します。
330             if (this.columnIndex > 0)
331             {
332                 this.WriteNewLine();
333             }
334             for (int row = 0; row < count; row++)
335             {
336                 if (!dataReader.Read())
337                 {
338                     break;
339                 }
340                 object[] values = null;
341                 if (names != null)
342                 {
343                     values = new object[names.Length];
344                     for (int nameIndex = 0; nameIndex < names.Length; nameIndex++)
345                     {
346                         values[nameIndex] = dataReader[names[nameIndex]];
347                     }
348                 }
349                 else
350                 {
351                     values = new object[dataReader.FieldCount];
352                     dataReader.GetValues(values);
353                 }
354                 foreach (object value in values)
355                 {
356                     if (value == DBNull.Value)
357                     {
358                         this.Write(string.Empty);
359                     }
360                     else if (value == null)
361                     {
362                         this.Write(string.Empty);
363                     }
364                     else
365                     {
366                         this.Write(value.ToString());
367                     }
368                 }
369                 this.WriteNewLine();
370             }
371         }
372     }
373 }