using (StreamReader sr = new StreamReader(fn, encoding, true))
{
StringBuilder sb = new StringBuilder();
bool quotMarkMode = false;
string newLineReplacement = "\x07";
string commaReplacement = "\x08";
//支援CSV雙引號內含換行符號規則,採逐字讀入解析
//雙引號內如需表示", 使用""代替
while (sr.Peek() >= 0)
{
var ch = (char)sr.Read();
if (quotMarkMode)
{
//雙引號包含區段內遇到雙引號有兩種情境
if (ch == '"')
{
//連續兩個雙引號,為欄位內雙引號字元
if (sr.Peek() == '"')
sb.Append((char)sr.Read());
//遇到結尾雙引號,雙引號包夾模式結束
else
quotMarkMode = false;
sb.Append(ch);
}
//雙引號內遇到換行符號,先置換成特殊字元,稍後換回
else if (ch == '\r' && sr.Peek() == '\n')
{
sr.Read();
sb.Append(newLineReplacement);
}
//雙引號內遇到逗號,先置換成特殊字元,稍後換回
else if (ch == ',')
sb.Append(commaReplacement);
//否則,正常插入字元
else
sb.Append(ch);
}
else
{
sb.Append(ch);
if (ch == '"') quotMarkMode = true;
}
}
var fixedCsv = sb.ToString();
sb.Length = 0;
string line;
using (var lr = new StringReader(fixedCsv))
{
while ((line = lr.ReadLine()) != null)
{
string[] p = line.Split(',');
sb.AppendLine(string.Join(",",
//若欄位以0起首,重新組裝成="...."格式
p.Select(o =>
o.StartsWith("0") ?
string.Format("=\"{0}\"", o) :
//還原換行符號及逗號
o.StartsWith("\"") ?
o.Replace(newLineReplacement, "\r\n")
.Replace(commaReplacement, ",") : o
).ToArray()));
}
}
//調整結果另存為同目錄下*.fixed.csv檔
string fixedFile = Path.Combine(
Path.GetDirectoryName(fn),
Path.GetFileNameWithoutExtension(fn) + ".fixed.csv");
//一律存為UTF8
File.WriteAllText(fixedFile, sb.ToString(), Encoding.UTF8);
//開啟CSV
Process proc = new Process();
proc.StartInfo = new ProcessStartInfo(fixedFile);
proc.Start();
}