[英]Using a reader to feed columns into a csv file
我有一個存儲過程,該過程將返回一列0或更多行或帳號。
如果有0行,我當然不需要做任何事情,但是如果返回1行或更多,則需要將這些帳號放入csv文件中。
返回的數據將如下所示:
100000
200000
286598
這是我的方法:
private static void ThirtyMinuteUpload(DateTime today)
{
using (SqlConnection connection = new SqlConnection(connString))
{
using (SqlCommand command = new SqlCommand("mySP", connection))
{
command.CommandType = CommandType.StoredProcedure;
connection.Open();
if (command.ExecuteReader().HasRows)
{
// Create csv
}
}
}
}
我需要將它們從我的閱讀器中放入csv中,並使用傳入的today
變量將文件命名為:
exlcusion_mmddyyhhmmss.csv
我從未使用過文件創建功能,現在可以將其保存到桌面上。 是容易做到的事情嗎?
我會做這樣的事情:
更新:修復了最后一個逗號問題。
using (SqlDataReader dr = command.ExecuteReader())
{
if (dr.HasRows())
{
string dateFormatted = today.ToString("MMddyyhhmmss");
string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
var writer = new StreamWriter(String.Format("{0}\exclusion_{1}.csv",
path, dateFormatted);
var cont = true;
while (cont)
{
// Grab the accountid before we read ahead
var accId = dr["accountid"];
// Read ahead to check if we've read the last record
cont = dr.Read();
// Last record, don't add comma
if (!cont)
{
writer.Write(accId);
}
else
{
writer.Write(accId + ",");
}
}
}
}
private static void ThirtyMinuteUpload(DateTime today)
{
using (var cn = new SqlConnection(connString))
using (var cmd = new SqlCommand("mySP", cn))
{
cmd.CommandType = CommandType.StoredProcedure;
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
if (!rdr.HasRows) return;
var fileName = string.Format("{0}{1}exclusion_{2:MMddyyHHmmss}.csv",
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
Path.PathSeparator,
today);
using (var writer = new StreamWriter(fileName))
{
while (rdr.Read())
{
writer.WriteLine(rdr.GetString(0));
}
}
}
}
}
這是您問題的更抽象答案。 我沒有使用SqlDataReader
,而是編寫了一個通用函數,該函數將從System.Data.IDataReader
任何實現者( System.Data.SqlClient.SqlDataReader
只是一個執行該操作的類)寫入數據到任何System.IO.Stream
,包括文件(使用FileStream
)。
/// <summary>
/// Writes the data from a given <see cref="IDataReader"/> <paramref name="reader"/> to the <paramref name="output"/> <see cref="Stream"/>.
/// There are optional parameters for writing a header, specifying the encoding, the buffer size, and whether or not the stream should be
/// closed when we're done reading.
/// </summary>
/// <param name="reader">Any object which implements <see cref="IDataReader"/>-- most likely a <see cref="System.Data.SqlClient.SqlDataReader"/>.</param>
/// <param name="output">The stream to output the CSV contents to.</param>
/// <param name="writeHeader">When true, a header is written using the column names.</param>
/// <param name="encoding">Optional parameter (defaulting to UTF8 without BOM) denoting how the data should be encoded.</param>
/// <param name="bufferSize">Optional parameter (defaulting to 1KB) which is used as a buffer for writing the data.</param>
/// <param name="closeOutput">Optional parameter which, when true, closes the <paramref name="output"/> <see cref="Stream"/> after we're doing writing.</param>
private static void WriteCsv(IDataReader reader, Stream output, bool writeHeader = true, Encoding encoding = null, int bufferSize = 1024, bool closeOutput = false)
{
// If no encoding is provided, use the same one the StreamWriter defaults to.
if (encoding == null)
encoding = new UTF8Encoding(false, true);
// Create a new writer to our CSV file.
using (var writer = new StreamWriter(output, encoding, bufferSize, !closeOutput))
{
// This will create an enumerable with every integer between 0 and FieldCount-1.
// Allows us to do a concise for loop and use String.Join to handle the comma placement.
var indices = Enumerable.Range(0, reader.FieldCount);
// Keep looping as long as their are rows returned by the reader.
while (reader.Read())
{
// Write a header with the names of each column.
if (writeHeader)
{
writer.WriteLine(String.Join(",", indices.Select(i => reader.GetName(i) ?? ("column" + i))));
writeHeader = false;
}
// Write the value of each field by its string representation separated by a comma.
writer.WriteLine(String.Join(",", indices.Select(i => (reader.IsDBNull(i) ? null : reader.GetString(i)) ?? "")));
}
}
}
此功能可讓您對某些細節(例如編碼)和要寫入的流進行大量控制(您可以寫入HTTP響應或常規文件,這無關緊要)。 如果您想將更復雜的數據輸出到CSV文件中,建議閱讀有關CSV“標准”的文章 。
這個編寫者很幼稚-只是寫入從IDataReader
讀取的原始數據。 如果您的內容包含換行符,回車符或逗號,則可能會混淆最終將占用程序輸出的任何內容。 我將編寫一個CsvEncode
函數,將每個值輸入其中,並根據上面文章中列出的規則對其正確編碼。
這只是一個示例,而不是您實際應該使用的代碼:
private static string CsvEncode(string value)
{
// Handle commas within values.
if (value.Contains(','))
{
// Strim so we get rid of beginning and trailing whitespaces we'd usually ignore.
value = value.Trim();
// If the value is already wrapped with quotation marks but has quotation marks within as well,
if (value.StartsWith("\"") && value.EndsWith("\"") && value.IndexOf('\"', 1, value.Length-2) > 0)
value = "\"" + value.Substring(1, value.Length - 2).Replace("\"", "\"\"") + "\"";
else if (value.Contains("\"")) // Replace all quotations with two quotations, then wrap the final result.
value = "\"" + value.Replace("\"", "\"\"") + "\"";
}
return value;
}
而且您只需更新WriteCsv
以便在寫入行的值時調用CsvEncode
,類似(僅作為示例):
// Write the value of each field by its string representation separated by a comma.
writer.WriteLine(String.Join(",", indices.Select(i => CsvEncode(reader.IsDBNull(i) ? "" : reader.GetString(i) ?? ""))));
為了更全面,這就是您的稱呼:
using (var reader = command.ExecuteReader())
{
if (!reader.HasRows)
return; // Nothing to do.
// You want it on the desktop? We'll put it on the desktop.
var filePath = string.Format("{0}{1}exclusion_{2:MMddyyHHmmss}.csv",
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
Path.PathSeparator,
today);
// Pass in the reader we got from executing the command. File.Create will replace any
// existing files. closeOutput is true because we do not keep a reference to the FileStream.
WriteCsv(reader, File.Create(filePath), closeOutput: true);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.