代码之家  ›  专栏  ›  技术社区  ›  Christian Hagelid

csv字符串处理

  •  17
  • Christian Hagelid  · 技术社区  · 16 年前

    创建 CSV 字符串(伪代码):

    1. 创建一个csv容器对象(就像c中的StringBuilder)。
    2. 循环遍历要添加的字符串,在每个字符串后添加一个逗号。
    3. 在循环之后,删除最后一个多余的逗号。

    代码示例:

    public string ReturnAsCSV(ContactList contactList)
    {
        StringBuilder sb = new StringBuilder();
        foreach (Contact c in contactList)
        {
            sb.Append(c.Name + ",");
        }
    
        sb.Remove(sb.Length - 1, 1);
        //sb.Replace(",", "", sb.Length - 1, 1)
    
        return sb.ToString();
    }
    

    我喜欢通过检查容器是否为空来添加逗号的想法,但这是否意味着需要进行更多的处理,因为它需要检查每次出现时字符串的长度?

    我觉得应该有一种更容易/更干净/更有效的方法来删除最后一个逗号。有什么想法吗?

    13 回复  |  直到 6 年前
        1
  •  19
  •   Matthew Groves    15 年前

    你可以使用 LINQ to Objects :

    string [] strings = contactList.Select(c => c.Name).ToArray();
    string csv = string.Join(",", strings);
    

    很明显,这一切都可以在一行中完成,但在两行中就更清楚了。

        2
  •  9
  •   Markus Safar    9 年前

    您的代码不符合 full CSV format . 如果您只是从没有逗号、前导/尾随空格、制表符、换行符或引号的数据生成csv,那么应该可以。然而,在大多数实际数据交换场景中,您确实需要完整的信息。

    要生成正确的csv,可以使用以下命令:

    public static String EncodeCsvLine(params String[] fields)
    {
        StringBuilder line = new StringBuilder();
    
        for (int i = 0; i < fields.Length; i++)
        {
            if (i > 0)
            {
                line.Append(DelimiterChar);
            }
    
            String csvField = EncodeCsvField(fields[i]);
            line.Append(csvField);
        }
    
        return line.ToString();
    }
    
    static String EncodeCsvField(String field)
    {
        StringBuilder sb = new StringBuilder();
        sb.Append(field);
    
        // Some fields with special characters must be embedded in double quotes
        bool embedInQuotes = false;
    
        // Embed in quotes to preserve leading/tralining whitespace
        if (sb.Length > 0 && 
            (sb[0] == ' ' || 
             sb[0] == '\t' ||
             sb[sb.Length-1] == ' ' || 
             sb[sb.Length-1] == '\t' ))
        {
            embedInQuotes = true;
        }
    
        for (int i = 0; i < sb.Length; i++)
        {
            // Embed in quotes to preserve: commas, line-breaks etc.
            if (sb[i] == DelimiterChar || 
                sb[i]=='\r' || 
                sb[i]=='\n' || 
                sb[i] == '"') 
            { 
                embedInQuotes = true;
                break;
            }
        }
    
        // If the field itself has quotes, they must each be represented 
        // by a pair of consecutive quotes.
        sb.Replace("\"", "\"\"");
    
        String rv = sb.ToString();
    
        if (embedInQuotes)
        {
            rv = "\"" + rv + "\"";
        }
    
        return rv;
    }
    

    可能不是世界上最有效的代码,但它已经过测试。与快速示例代码相比,现实世界很糟糕:)

        3
  •  5
  •   yoliho    16 年前

    为什么不使用一个开源的csv库呢?

    我知道这听起来像是对一些看起来很简单的东西的过度杀戮,但是正如你从评论和代码片段中所看到的,有很多东西值得一看。除了处理完整的csv遵从性之外,您最终还需要处理读写csv…您可能需要文件操作。

    我已经用过 Open CSV 在我以前的一个项目上(但还有很多其他项目可供选择)。这确实让我的生活更轻松了。;)

        4
  •  5
  •   Markus Safar    9 年前

    别忘了我们的老朋友“为了”。它不像foreach那么好看,但是它的优点是能够从第二个元素开始。

    public string ReturnAsCSV(ContactList contactList)
    {
        if (contactList == null || contactList.Count == 0)
            return string.Empty;
    
        StringBuilder sb = new StringBuilder(contactList[0].Name);
    
        for (int i = 1; i < contactList.Count; i++)
        {
            sb.Append(",");
            sb.Append(contactList[i].Name);
        }
    
        return sb.ToString();
    }
    

    您还可以将第二个附加包装在“if”中,该“if”用于测试name属性是否包含双引号或逗号,如果包含双引号或逗号,则相应地对其进行转义。

        5
  •  3
  •   Developer    13 年前

    你可以把逗号作为你前臂里的第一个东西。

    if (sb.Length > 0) sb.Append(",");

        6
  •  3
  •   Markus Safar    9 年前

    您还可以对 C.姓名 数据与使用 连接字符串 创建线条的方法。

    public string ReturnAsCSV(ContactList contactList)
    {
        List<String> tmpList = new List<string>();
    
        foreach (Contact c in contactList)
        {
            tmpList.Add(c.Name);
        }
    
        return String.Join(",", tmpList.ToArray());
    }
    

    这可能不如 字符串拼接 接近,但看起来确实更干净。

    另外,您可能需要考虑使用 .CurrentCulture.TextInfo.ListSeparator 而不是硬编码的逗号——如果要将输出导入到其他应用程序中,则可能会遇到问题。ListSeparator可能在不同的文化中有所不同,而MS至少擅长这种设置。所以:

    return String.Join(
        System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator,
        tmpList.ToArray());
    
        7
  •  1
  •   Xenph Yan    16 年前

    我喜欢通过检查容器是否为空来添加逗号的想法,但这是否意味着需要进行更多的处理,因为它需要检查每次出现时字符串的长度?

    你过早的优化,性能的影响可以忽略不计。

        8
  •  1
  •   erlando    16 年前

    只是一个想法,但是记住在字段值中处理逗号和引号(“),否则您的csv文件可能会破坏消费者阅读器。

        9
  •  1
  •   jocull    12 年前

    我为这个写了一个小班,以防别人发现它有用…

    public class clsCSVBuilder
    {
        protected int _CurrentIndex = -1;
        protected List<string> _Headers = new List<string>();
        protected List<List<string>> _Records = new List<List<string>>();
        protected const string SEPERATOR = ",";
    
        public clsCSVBuilder() { }
    
        public void CreateRow()
        {
            _Records.Add(new List<string>());
            _CurrentIndex++;
        }
    
        protected string _EscapeString(string str)
        {
            return string.Format("\"{0}\"", str.Replace("\"", "\"\"")
                                                .Replace("\r\n", " ")
                                                .Replace("\n", " ")
                                                .Replace("\r", " "));
        }
    
        protected void _AddRawString(string item)
        {
            _Records[_CurrentIndex].Add(item);
        }
    
        public void AddHeader(string name)
        {
            _Headers.Add(_EscapeString(name));
        }
    
        public void AddRowItem(string item)
        {
            _AddRawString(_EscapeString(item));
        }
    
        public void AddRowItem(int item)
        {
            _AddRawString(item.ToString());
        }
    
        public void AddRowItem(double item)
        {
            _AddRawString(item.ToString());
        }
    
        public void AddRowItem(DateTime date)
        {
            AddRowItem(date.ToShortDateString());
        }
    
        public static string GenerateTempCSVPath()
        {
            return Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString().ToLower().Replace("-", "") + ".csv");
        }
    
        protected string _GenerateCSV()
        {
            StringBuilder sb = new StringBuilder();
    
            if (_Headers.Count > 0)
            {
                sb.AppendLine(string.Join(SEPERATOR, _Headers.ToArray()));
            }
    
            foreach (List<string> row in _Records)
            {
                sb.AppendLine(string.Join(SEPERATOR, row.ToArray()));
            }
    
            return sb.ToString();
        }
    
        public void SaveAs(string path)
        {
            using (StreamWriter sw = new StreamWriter(path))
            {
                sw.Write(_GenerateCSV());
            }
        }
    }
    
        10
  •  1
  •   Markus Safar    9 年前

    我以前用过这种方法。StringBuilder的长度属性不是只读的,因此用一种方法减去它将截断最后一个字符。但是您必须确保您的长度不是零(如果您的列表是空的,则会发生这种情况),因为将长度设置为小于零是一个错误。

    public string ReturnAsCSV(ContactList contactList)
    {
        StringBuilder sb = new StringBuilder();
    
        foreach (Contact c in contactList)       
        { 
            sb.Append(c.Name + ",");       
        }
    
        if (sb.Length > 0)  
            sb.Length -= 1;
    
        return sb.ToString();  
    }
    
        11
  •  0
  •   Ed Power    12 年前

    我用 CSVHelper -它是一个伟大的开放源码库,可以让您一次生成一个兼容的csv流元素或自定义映射类:

    public string ReturnAsCSV(ContactList contactList)
    {
        StringBuilder sb = new StringBuilder();
        using (StringWriter stringWriter = new StringWriter(sb))
        {
            using (var csvWriter = new CsvHelper.CsvWriter(stringWriter))
            {
                csvWriter.Configuration.HasHeaderRecord = false;
                foreach (Contact c in contactList)
                {
                    csvWriter.WriteField(c.Name);
                }
            }
        }
        return sb.ToString();
    }
    

    或者如果你绘制了这样的地图: csvWriter.WriteRecords<ContactList>(contactList);

        12
  •  0
  •   Markus Safar    9 年前

    修剪一下怎么样?

    public string ReturnAsCSV(ContactList contactList)
    {
        StringBuilder sb = new StringBuilder();
    
        foreach (Contact c in contactList)
        {
            sb.Append(c.Name + ",");
        }
    
        return sb.ToString().Trim(',');
    }
    
        13
  •  0
  •   Markus Safar    9 年前

    跟踪你是否在第一个项目上,只加一个逗号怎么样 之前 如果不是第一个项目,则为该项目。

    public string ReturnAsCSV(ContactList contactList)
    {
        StringBuilder sb = new StringBuilder();
        bool isFirst = true;
    
        foreach (Contact c in contactList) {
            if (!isFirst) { 
              // Only add comma before item if it is not the first item
              sb.Append(","); 
            } else {
              isFirst = false;
            }
    
            sb.Append(c.Name);
        }
    
        return sb.ToString();
    }