iT邦幫忙

0

C# ASP.Net 匯出CSV檔無法輸出逗號

問題: 匯出檔案.CSV時希望匯出逗號
已經搜尋過網路,說在要保留逗號,的字串前後加雙引號"string"
可是怎麼加雙引號都沒用不知道是哪裡錯了QQ
看不出來哪裡有問題還請各位幫忙><謝謝

還請各位幫忙看一下是哪裡的問題
原始資料是有幾行的資料內容有逗號

 while (!streamReader.EndOfStream)
    {
        string oneLine = streamReader.ReadLine();  // 一行
        string streamWriter = "";
        string address = "";
        string[] oneLineArr = Regex.Split(oneLine, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"); // 分割字串用逗號,但雙引號內的逗號不動


        streamWriter = $"姓名:{oneLineArr[3]}電話: {oneLineArr[5]}地址:";

        if (oneLine.Contains("\"")) // 如果有雙引號
        {
           Regex reg = new Regex("\"[^\"]*\"");                  // 取前後雙引號裡的字串
          address = reg.Match(oneLine).Value.Replace("\"", ""); // 將原本資料中前後帶雙引號的取代掉
            
            streamWriter += "\"" + address + "\"\r\n";         // 這個不行
            streamWriter += String.Format("{0}{1}\"{2}\"", oneLineArr[7], oneLineArr[8], address); // 這個也不行
            streamWriter += String.Format("{0}{1}{2}", oneLineArr[7], oneLineArr[8], address); // 這個也不行
            streamWriter += String.Format("{0}", "\"" + address + "\""); // 這個也不行
        }
        else
        {
            streamWriter += oneLineArr[7] + oneLineArr[8] + oneLineArr[9];
        }

        newFileStreamWrite.WriteLine(streamWriter);

圖中有雙引號的都被欄位分割了!!可是就不要分割啊!!要保留逗號啊!!
https://ithelp.ithome.com.tw/upload/images/20201221/201205582joHN2WL7f.png

這是notepad++開的檔案
https://ithelp.ithome.com.tw/upload/images/20201221/20120558nwN6JCemKN.png

網路上多半解法都是加 雙引號 "string" 可是還是抓不到
https://stackoverflow.com/questions/5740359/how-to-use-comma-in-csv-columns

已經用好久了還是不行.....
謝謝大神們

回覆在下方
1
㊣浩瀚星空㊣
iT邦超人 1 級 ‧ 2020-12-21 14:27:23
最佳解答

從你po出來的圖片看。你只是用""包起來。
但你並沒使用正確的格式。

如你

台北市"aaa.708 ,JJJ..."

但正確的格式是

台北市,"aaa.708 ,JJJ..."
或
"台北市aaa.708 ,JJJ..."

(因為我不懂你那個台北市是要當一欄還是放在一起。)

雙引號要包的是一整欄的資料。
如果你用第一種方式包的話。
你會害excel不懂這是什麼東東。就只能依照你的,分開了。

其實如果你的欄位中沒有數值型態的話,大可以將所有的欄位都用雙引號包起來。
也不需要再用正則處理了。

0
japhenchen
iT邦大師 1 級 ‧ 2020-12-21 16:38:39

這樣改看看

string Pattern = "\"[^\"]*,[^\"]*\"";
Regex commaSpl = new Regex(Pattern, RegexOptions.Compiled);
List<string> outputCSV = new List<string>();
string FileToRead = @"s:\pi\test.csv";
using (StreamReader ReaderObject = new StreamReader(FileToRead))
{
string Line;
string oline = "";
string[] splitLine;
while ((Line = ReaderObject.ReadLine()) != null)
{
    oline = "";
    if (commaSpl.IsMatch(Line))
    {
        splitLine = Line.Split(new string[] { "\n" }, StringSplitOptions.None);
    }
    else
    {
        splitLine = Line.Split(new string[] { "," }, StringSplitOptions.None);
    }
    foreach (var s in splitLine)
        oline += $"\"{s.Replace("\"", "")}\",";
    outputCSV.Add(oline);
}

File.WriteAllText(@"s:\pi\new.csv", string.Join("\n", outputCSV));
}

看更多先前的回應...收起先前的回應...

糟,剛剛發現我沒有考慮到 "xxxxx,yyyy","zzzz" 的狀況

直接教他判斷是否數值。數值就不要包""
如果有包到"字串的話。我記得要用兩個"才行。用"好像不行。

.net我不會處理。所以沒辦法寫給他看。
只能告訴他理念。

只是不明白為何要每個欄位都要包""

chang137 iT邦新手 5 級 ‧ 2020-12-22 08:57:29 檢舉

現在用的截取程式段 內含參考來源
private static string[] SplitComma(string subjectString)
{
List result = new List();
// ref url: https://stackoverflow.com/questions/3268622/regex-to-split-line-csv-file
Regex rgx = new Regex(@"
# Parse CVS line. Capture next value in named group: 'val'
\s* # Ignore leading whitespace.
(?: # Group of value alternatives.
"" # Either a double quoted string,
(? # Capture contents between quotes.
[^""](""""[^""])* # Zero or more non-quotes, allowing
) # doubled "" quotes within string.
""\s* # Ignore whitespace following quote.
| (?[^,]*) # Or... zero or more non-commas.
) # End value alternatives group.
(?:,|$) # Match end is comma or EOS",
RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace);
MatchCollection matches = rgx.Matches(subjectString);
foreach (Match mtch in matches)
{
if (mtch.Success && mtch.Index < subjectString.Length)
{
string value = mtch.Value.TrimEnd(',');
if (value.Length >= 2 && value.First() == '"' && value.Last() == '"')
value = value.Trim('"').Replace("""", """);
result.Add(value);
}
}
return result.ToArray();
}

因為""其實對csv來說,就是宣告字串格式的欄位。
認真來說,跟用陣列的用法很像。
用雙引號包起來的會視為一個欄位內容。

直接用雙引號包全部的欄位,就可以省下判斷字元處理。
(雖然內容中的雙引號還是需要處理就是了)

算是很懶的招。

像 chang137 po的,就是針對csv全部格式轉換的方式。
我只是教最簡單的處理。
但 chang137 是更完美的處理方式。

我幫忙將程式碼整理一下

private static string[] SplitComma(string subjectString)
{
    List result = new List();
    // ref url: https://stackoverflow.com/questions/3268622/regex-to-split-line-csv-file
    Regex rgx = new Regex(@"
        # Parse CVS line. Capture next value in named group: 'val'
        \s* # Ignore leading whitespace.
        (?: # Group of value alternatives.
        "" # Either a double quoted string,
        (? # Capture contents between quotes.
        [^""](""""[^""])* # Zero or more non-quotes, allowing
        ) # doubled "" quotes within string.
        ""\s* # Ignore whitespace following quote.
        | (?[^,]*) # Or... zero or more non-commas.
    ) # End value alternatives group.
    (?:,|$) # Match end is comma or EOS",
    RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace);
    MatchCollection matches = rgx.Matches(subjectString);
    foreach (Match mtch in matches)
    {
        if (mtch.Success && mtch.Index < subjectString.Length)
        {
            string value = mtch.Value.TrimEnd(',');
            if (value.Length >= 2 && value.First() == '"' && value.Last() == '"')
            value = value.Trim('"').Replace("""", """);
            result.Add(value);
        }
    }
    return result.ToArray();
}
0

是不是要將從 CSV 取得的欄位合併成一個呢?

如果是的話,雙引號要串在整個字串的開頭和結尾,例如:

小明,09xxxxxxxx,"abc,123"

轉成

"小明 09xxxxxxxx abc,123"

程式碼:

var result = "";
using (var streamReader = new StreamReader("test.csv"))
{
    //切割欄位的正規式
    var regex = new Regex("(?<=^|,)(\"(?:[^\"]|\"\")*\"|[^,]*)", 
        RegexOptions.Multiline);

    //去除前後的雙引號
    IEnumerable<string> clear(MatchCollection matches)
    {
        foreach(Match match in matches)
        {
            //去除頭尾空白
            var value = match.Value.Trim();
            //如果開頭是雙引號,就去除頭尾的雙引號
            yield return value.StartsWith("\"") ?
                match.Value.Substring(1, match.Value.Length - 2) : value;
        }
    };

    while (!streamReader.EndOfStream)
    {
        var line = streamReader.ReadLine();

        //切割欄位
        var matches = regex.Matches(line);
        //清理資料
        var datas = clear(matches).ToList();

        //合併各欄位
        result += $"\"姓名: {datas[0]} 電話: {datas[1]} 地址: {datas[2]}\"\n";
    }
    File.WriteAllText(@"new.csv", result, Encoding.UTF8);
}

正規式部分參考 chang137 提供的連結:
https://stackoverflow.com/questions/3268622/regex-to-split-line-csv-file

結果:

原始資料

小明,09xxxxxxxx,"abc,""123"
老王,09xxxxx123,台中市

轉換後的資料

"姓名: 小明 電話: 09xxxxxxxx 地址: abc,""123"
"姓名: 老王 電話: 09xxxxx123 地址: 台中市"

Excel 開啟

https://ithelp.ithome.com.tw/upload/images/20201222/20106865qSQSQZZ1Rc.jpg


不過使用套件比較好啦,寫法也比較直覺。

Nuget 安裝 CsvHelper

程式碼:

public class Input
{
    public string Name { get; set; }
    public string Phone { get; set; }
    public string Address { get; set; }
}

public class Output
{
    public string Data { get; set; }
}

static void Main(string[] args)
{
    var input = null as List<Input>;

    //讀取資料
    using (var reader = new StreamReader("test.csv", Encoding.UTF8))
    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
    {
        csv.Configuration.HasHeaderRecord = false;

        input = csv.GetRecords<Input>().ToList();
    }

    //寫入資料
    using (var writer = new StreamWriter("new2.csv", false, Encoding.UTF8))
    using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
    {
        csv.Configuration.HasHeaderRecord = false;

        var output = input.Select(it => new Output
        {
            Data = $"姓名: {it.Name} 電話: {it.Phone} 地址: {it.Address}"
        });
        csv.WriteRecords(output);
    }
}

轉換後的資料

"姓名: 小明 電話: 09xxxxxxxx 地址: abc,""123"
姓名: 老王 電話: 09xxxxx123 地址: 台中市

Excel 開啟

https://ithelp.ithome.com.tw/upload/images/20201222/20106865IB5wpJOAGe.jpg

我要發表回答

立即登入回答