<p>Typical way of creating a <a href="http://en.wikipedia.org/wiki/Comma-separated_values" rel="noreferrer">CSV</a> string (pseudocode):</p>

<ol>
<li>Create a CSV container object (like a StringBuilder in C#).</li>
<li>Loop through the strings you want to add appending a comma after each one.</li>
<li>After the loop, remove that last superfluous comma.</li>
</ol>

<p>Code sample:</p>

<pre><code>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();
}
</code></pre>

<p>I like the idea of adding the comma by checking if the container is empty, but doesn't that mean more processing as it needs to check the length of the string on each occurrence?</p>

<p>I feel that there should be an easier/cleaner/more efficient way of removing that last comma. Any ideas?</p>

[{'answer_id': 4437, 'author': 'Chris Farmer', 'author_id': 404, 'author_profile': 'https://Stackoverflow.com/users/404', 'pm_score': 2, 'selected': False, 'text': '<p>You could instead add the comma as the first thing inside your foreach.</p>\n\n<p><code>if (sb.Length &gt; 0) sb.Append(",");</code></p>\n'}, {'answer_id': 4438, 'author': 'Yaakov Ellis', 'author_id': 51, 'author_profile': 'https://Stackoverflow.com/users/51', 'pm_score': 0, 'selected': False, 'text': '<p>How about tracking whether you are on the first item, and only add a comma <em>before</em> the item if it is not the first one.</p>\n\n<pre><code>public string ReturnAsCSV(ContactList contactList)\n{\n    StringBuilder sb = new StringBuilder();\n    bool isFirst = true;\n\n    foreach (Contact c in contactList) {\n        if (!isFirst) { \n          // Only add comma before item if it is not the first item\n          sb.Append(","); \n        } else {\n          isFirst = false;\n        }\n\n        sb.Append(c.Name);\n    }\n\n    return sb.ToString();\n}\n</code></pre>\n'}, {'answer_id': 4441, 'author': 'David Wengier', 'author_id': 489, 'author_profile': 'https://Stackoverflow.com/users/489', 'pm_score': 5, 'selected': True, 'text': '<p>You could use <a href="http://en.wikipedia.org/wiki/Language_Integrated_Query#LINQ_to_Objects" rel="noreferrer">LINQ to Objects</a>:</p>\n\n<pre><code>string [] strings = contactList.Select(c =&gt; c.Name).ToArray();\nstring csv = string.Join(",", strings);\n</code></pre>\n\n<p>Obviously that could all be done in one line, but it\'s a bit clearer on two.</p>\n'}, {'answer_id': 4461, 'author': 'Xenph Yan', 'author_id': 264, 'author_profile': 'https://Stackoverflow.com/users/264', 'pm_score': 1, 'selected': False, 'text': "<blockquote>\n  <p>I like the idea of adding the comma by checking if the container is empty, but doesn't that mean more processing as it needs to check the length of the string on each occurrence?</p>\n</blockquote>\n\n<p>You're prematurely optimizing, the performance hit would be negligible.</p>\n"}, {'answer_id': 4502, 'author': 'Ishmaeel', 'author_id': 227, 'author_profile': 'https://Stackoverflow.com/users/227', 'pm_score': 2, 'selected': False, 'text': '<p>You could also make an array of <strong>c.Name</strong> data and use <strong>String.Join</strong> method to create your line.</p>\n\n<pre><code>public string ReturnAsCSV(ContactList contactList)\n{\n    List&lt;String&gt; tmpList = new List&lt;string&gt;();\n\n    foreach (Contact c in contactList)\n    {\n        tmpList.Add(c.Name);\n    }\n\n    return String.Join(",", tmpList.ToArray());\n}\n</code></pre>\n\n<p>This might not be as performant as the <strong>StringBuilder</strong> approach, but it definitely looks cleaner.</p>\n\n<p>Also, you might want to consider using <strong>.CurrentCulture.TextInfo.ListSeparator</strong> instead of a hard-coded comma -- If your output is going to be imported into other applications, you might have problems with it. ListSeparator may be different across different cultures, and MS Excel at the very least, honors this setting. So:</p>\n\n<pre><code>return String.Join(\n    System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator,\n    tmpList.ToArray());\n</code></pre>\n'}, {'answer_id': 4518, 'author': 'GateKiller', 'author_id': 383, 'author_profile': 'https://Stackoverflow.com/users/383', 'pm_score': 0, 'selected': False, 'text': '<p>How about some trimming?</p>\n\n<pre><code>public string ReturnAsCSV(ContactList contactList)\n{\n    StringBuilder sb = new StringBuilder();\n\n    foreach (Contact c in contactList)\n    {\n        sb.Append(c.Name + ",");\n    }\n\n    return sb.ToString().Trim(\',\');\n}\n</code></pre>\n'}, {'answer_id': 4592, 'author': 'Kev', 'author_id': 419, 'author_profile': 'https://Stackoverflow.com/users/419', 'pm_score': 1, 'selected': False, 'text': '<p>Just a thought, but remember to handle comma\'s and quotation marks (") in the field values, otherwise your CSV file may break the consumers reader.</p>\n'}, {'answer_id': 4611, 'author': 'Matt Hamilton', 'author_id': 615, 'author_profile': 'https://Stackoverflow.com/users/615', 'pm_score': 3, 'selected': False, 'text': '<p>Don\'t forget our old friend "for". It\'s not as nice-looking as foreach but it has the advantage of being able to start at the second element.</p>\n\n<pre><code>public string ReturnAsCSV(ContactList contactList)\n{\n    if (contactList == null || contactList.Count == 0)\n        return string.Empty;\n\n    StringBuilder sb = new StringBuilder(contactList[0].Name);\n\n    for (int i = 1; i &lt; contactList.Count; i++)\n    {\n        sb.Append(",");\n        sb.Append(contactList[i].Name);\n    }\n\n    return sb.ToString();\n}\n</code></pre>\n\n<p>You could also wrap the second Append in an "if" that tests whether the Name property contains a double-quote or a comma, and if so, escape them appropriately.</p>\n'}, {'answer_id': 6724, 'author': 'dbkk', 'author_id': 838, 'author_profile': 'https://Stackoverflow.com/users/838', 'pm_score': 3, 'selected': False, 'text': '<p>Your code not really compliant with <a href="http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm" rel="nofollow noreferrer">full CSV format</a>. If you are just generating CSV from data that has no commas, leading/trailing spaces, tabs, newlines or quotes, it should be fine. However, in most real-world data-exchange scenarios, you do need the full imlementation. </p>\n\n<p>For generation to proper CSV, you can use this:</p>\n\n<pre><code>public static String EncodeCsvLine(params String[] fields)\n{\n    StringBuilder line = new StringBuilder();\n\n    for (int i = 0; i &lt; fields.Length; i++)\n    {\n        if (i &gt; 0)\n        {\n            line.Append(DelimiterChar);\n        }\n\n        String csvField = EncodeCsvField(fields[i]);\n        line.Append(csvField);\n    }\n\n    return line.ToString();\n}\n\nstatic String EncodeCsvField(String field)\n{\n    StringBuilder sb = new StringBuilder();\n    sb.Append(field);\n\n    // Some fields with special characters must be embedded in double quotes\n    bool embedInQuotes = false;\n\n    // Embed in quotes to preserve leading/tralining whitespace\n    if (sb.Length &gt; 0 &amp;&amp; \n        (sb[0] == \' \' || \n         sb[0] == \'\\t\' ||\n         sb[sb.Length-1] == \' \' || \n         sb[sb.Length-1] == \'\\t\' ))\n    {\n        embedInQuotes = true;\n    }\n\n    for (int i = 0; i &lt; sb.Length; i++)\n    {\n        // Embed in quotes to preserve: commas, line-breaks etc.\n        if (sb[i] == DelimiterChar || \n            sb[i]==\'\\r\' || \n            sb[i]==\'\\n\' || \n            sb[i] == \'"\') \n        { \n            embedInQuotes = true;\n            break;\n        }\n    }\n\n    // If the field itself has quotes, they must each be represented \n    // by a pair of consecutive quotes.\n    sb.Replace("\\"", "\\"\\"");\n\n    String rv = sb.ToString();\n\n    if (embedInQuotes)\n    {\n        rv = "\\"" + rv + "\\"";\n    }\n\n    return rv;\n}\n</code></pre>\n\n<p>Might not be world\'s most efficient code, but it has been tested. Real world sucks compared to quick sample code :)</p>\n'}, {'answer_id': 17305, 'author': 'Autodidact', 'author_id': 2051, 'author_profile': 'https://Stackoverflow.com/users/2051', 'pm_score': 1, 'selected': False, 'text': '<p>I\'ve used this method before. The Length property of StringBuilder is NOT readonly so subtracting it by one means truncate the last character. But you have to make sure your length is not zero to start with (which would happen if your list is empty) because setting the length to less than zero is an error.</p>\n\n<pre><code>public string ReturnAsCSV(ContactList contactList)\n{\n    StringBuilder sb = new StringBuilder();\n\n    foreach (Contact c in contactList)       \n    { \n        sb.Append(c.Name + ",");       \n    }\n\n    if (sb.Length &gt; 0)  \n        sb.Length -= 1;\n\n    return sb.ToString();  \n}\n</code></pre>\n'}, {'answer_id': 17340, 'author': 'yoliho', 'author_id': 237, 'author_profile': 'https://Stackoverflow.com/users/237', 'pm_score': 3, 'selected': False, 'text': '<p><strong>Why not use one of the open source CSV libraries out there?</strong></p>\n\n<p>I know it sounds like overkill for something that appears so simple, but as you can tell by the comments and code snippets, there\'s more than meets the eye.  In addition to handling full CSV compliance, you\'ll eventually want to handle both reading and writing CSVs... and you may want file manipulation.</p>\n\n<p>I\'ve used <a href="http://opencsv.sourceforge.net/" rel="noreferrer">Open CSV</a> on one of my projects before (but there are plenty of others to choose from).  It certainly made my life easier. ;)</p>\n'}, {'answer_id': 11214712, 'author': 'jocull', 'author_id': 97964, 'author_profile': 'https://Stackoverflow.com/users/97964', 'pm_score': 1, 'selected': False, 'text': '<p>I wrote a small class for this in case someone else finds it useful...</p>\n\n<pre><code>public class clsCSVBuilder\n{\n    protected int _CurrentIndex = -1;\n    protected List&lt;string&gt; _Headers = new List&lt;string&gt;();\n    protected List&lt;List&lt;string&gt;&gt; _Records = new List&lt;List&lt;string&gt;&gt;();\n    protected const string SEPERATOR = ",";\n\n    public clsCSVBuilder() { }\n\n    public void CreateRow()\n    {\n        _Records.Add(new List&lt;string&gt;());\n        _CurrentIndex++;\n    }\n\n    protected string _EscapeString(string str)\n    {\n        return string.Format("\\"{0}\\"", str.Replace("\\"", "\\"\\"")\n                                            .Replace("\\r\\n", " ")\n                                            .Replace("\\n", " ")\n                                            .Replace("\\r", " "));\n    }\n\n    protected void _AddRawString(string item)\n    {\n        _Records[_CurrentIndex].Add(item);\n    }\n\n    public void AddHeader(string name)\n    {\n        _Headers.Add(_EscapeString(name));\n    }\n\n    public void AddRowItem(string item)\n    {\n        _AddRawString(_EscapeString(item));\n    }\n\n    public void AddRowItem(int item)\n    {\n        _AddRawString(item.ToString());\n    }\n\n    public void AddRowItem(double item)\n    {\n        _AddRawString(item.ToString());\n    }\n\n    public void AddRowItem(DateTime date)\n    {\n        AddRowItem(date.ToShortDateString());\n    }\n\n    public static string GenerateTempCSVPath()\n    {\n        return Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString().ToLower().Replace("-", "") + ".csv");\n    }\n\n    protected string _GenerateCSV()\n    {\n        StringBuilder sb = new StringBuilder();\n\n        if (_Headers.Count &gt; 0)\n        {\n            sb.AppendLine(string.Join(SEPERATOR, _Headers.ToArray()));\n        }\n\n        foreach (List&lt;string&gt; row in _Records)\n        {\n            sb.AppendLine(string.Join(SEPERATOR, row.ToArray()));\n        }\n\n        return sb.ToString();\n    }\n\n    public void SaveAs(string path)\n    {\n        using (StreamWriter sw = new StreamWriter(path))\n        {\n            sw.Write(_GenerateCSV());\n        }\n    }\n}\n</code></pre>\n'}, {'answer_id': 11215149, 'author': 'Ed Power', 'author_id': 150058, 'author_profile': 'https://Stackoverflow.com/users/150058', 'pm_score': 0, 'selected': False, 'text': '<p>I use <a href="https://github.com/JoshClose/CsvHelper" rel="nofollow">CSVHelper</a> - it\'s a great open-source library that lets you generate compliant CSV streams one element at a time or custom-map your classes:</p>\n\n<pre><code>public string ReturnAsCSV(ContactList contactList)\n{\n    StringBuilder sb = new StringBuilder();\n    using (StringWriter stringWriter = new StringWriter(sb))\n    {\n        using (var csvWriter = new CsvHelper.CsvWriter(stringWriter))\n        {\n            csvWriter.Configuration.HasHeaderRecord = false;\n            foreach (Contact c in contactList)\n            {\n                csvWriter.WriteField(c.Name);\n            }\n        }\n    }\n    return sb.ToString();\n}\n</code></pre>\n\n<p>or if you map then something like this: <code>csvWriter.WriteRecords&lt;ContactList&gt;(contactList);</code></p>\n'}]
['https://Stackoverflow.com/questions/4432', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/202/']