72

I have an object which holds many values, and some of them (not all values from the object) need to be put in a CSV string. My approach was this:

string csvString = o.number + "," + o.id + "," + o.whatever ....

Is there is a better, more elegant way?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
grady
  • 12,281
  • 28
  • 71
  • 110
  • 1
    Generally speaking it's considered less efficient to concatenate strings with the `+` symbol. It creates more objects to be garbage collected. – Phil Gan Feb 03 '11 at 09:33

6 Answers6

144

If you put all your values in an array, at least you can use string.Join.

string[] myValues = new string[] { ... };
string csvString = string.Join(",", myValues);

You can also use the overload of string.Join that takes params string as the second parameter like this:

string csvString = string.Join(",", value1, value2, value3, ...);
Øyvind Bråthen
  • 59,338
  • 27
  • 124
  • 151
  • +1 for using an array in between which keeps the code more legible when there are more than "a few" values. – Bazzz Feb 03 '11 at 09:09
  • @Bazzz - Yes, it might quickly get messy if you enter 10+ parameters into the join method call. I think the first approach is the cleanest one, but for a few values, the second one is also ok. – Øyvind Bråthen Feb 03 '11 at 09:12
  • Why two downvotes on this answer? A reason would be appreciated so everyone knows what you feel are the problem with this answer. – Øyvind Bråthen Feb 03 '11 at 09:13
  • Yeah one was from me (sorry lol). He doesn't have his values in an array and it's just unnecessary to use an array as an intermediate step to building the string. It's inefficient and makes the code longer (and imho no more readable). I prefer just using the `Join` directly (as your update). – fearofawhackplanet Feb 03 '11 at 09:19
  • 1
    @fearofawhackplanet - Thanks for giving a reason. Gives more sense that way ;) You are of course entiteled to your own opinion. I just like to hear it ;) – Øyvind Bråthen Feb 03 '11 at 09:27
  • +1 for using an array; it's neater and functionality can be extended more easily. – Phil Gan Feb 03 '11 at 09:29
  • 4
    @fear: `string.Join` uses a params array. Meaning code that passes the parameters in directly will be converted to use an array automatically by the compiler. There is no difference between the calls as far as overheads are concerned. – Jeff Mercado Feb 03 '11 at 19:37
  • This is not strictly the correct answer for CSV strings. True CSV will escape strings as needed. – jgmjgm Sep 28 '15 at 14:33
  • What should I do if value contains comma, for e.g. I've values like test1, test2 and test, test3 where test, test3 should be consider as a single value. – Nimesh khatri May 06 '21 at 08:20
15

Another approach is to use the CommaDelimitedStringCollection class from System.Configuration namespace/assembly. It behaves like a list plus it has an overriden ToString method that returns a comma-separated string.

Pros - More flexible than an array.

Cons - You can't pass a string containing a comma.

CommaDelimitedStringCollection list = new CommaDelimitedStringCollection();

list.AddRange(new string[] { "Huey", "Dewey" });
list.Add("Louie");
//list.Add(",");

string s = list.ToString(); //Huey,Dewey,Louie
grysik44
  • 233
  • 2
  • 7
5

You can use the string.Join method to do something like string.Join(",", o.Number, o.Id, o.whatever, ...).

edit: As digEmAll said, string.Join is faster than StringBuilder. They use an external implementation for the string.Join.

Profiling code (of course run in release without debug symbols):

class Program
{
    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
        string r;
        int iter = 10000;

        string[] values = { "a", "b", "c", "d", "a little bit longer please", "one more time" };

        sw.Restart();
        for (int i = 0; i < iter; i++)
            r = Program.StringJoin(",", values);
        sw.Stop();
        Console.WriteLine("string.Join ({0} times): {1}ms", iter, sw.ElapsedMilliseconds);

        sw.Restart();
        for (int i = 0; i < iter; i++)
            r = Program.StringBuilderAppend(",", values);
        sw.Stop();
        Console.WriteLine("StringBuilder.Append ({0} times): {1}ms", iter, sw.ElapsedMilliseconds);
        Console.ReadLine();
    }

    static string StringJoin(string seperator, params string[] values)
    {
        return string.Join(seperator, values);
    }

    static string StringBuilderAppend(string seperator, params string[] values)
    {
        StringBuilder builder = new StringBuilder();
        builder.Append(values[0]);
        for (int i = 1; i < values.Length; i++)
        {
            builder.Append(seperator);
            builder.Append(values[i]);
        }
        return builder.ToString();
    }
}

string.Join took 2ms on my machine and StringBuilder.Append 5ms. So there is noteworthy difference. Thanks to digAmAll for the hint.

jb_
  • 958
  • 5
  • 13
  • 2
    `string.Join` is as fast as `StringBuilder` (if not slightly faster) because they both allocate one string only – digEmAll Feb 03 '11 at 09:07
  • The problem with normal string concat (eg. using string1 += string2) is that the original string1 is thrown away (since strings are immutable), and the new sum of string1 and string2 is pointed by string1, and that is not very efficient if done repeatedly. However, as digEmAll also points out, string.Join of course only allocate the string once. Not once for each element in the array. – Øyvind Bråthen Feb 03 '11 at 09:11
  • One disadvantage of `string.Join` (until .NET 4.0) is that it requires an array of strings, forcing you to allocate one if you have only a generic `IEnumerable`... anyway .NET 4.0 has fixed this problem – digEmAll Feb 03 '11 at 09:15
  • You are right I edited my post. But I wonder which external method is called for string.Join as it is a native implementation and not inside mscorlib? – jb_ Feb 03 '11 at 09:21
  • @jb_: actually (my fault) I checked only .net 2 `string.Join` implementation, and those 2 overloads (taking `string[]`) make use of an external method called `FastAllocateString` that is (maybe sometimes) slightly faster. Surprisingly (for me), the new `string.Join` overloads introduced with .net 4, simply use a `StringBuilder` internally! So basically there's no difference between the 2 methods, then, my suggestion is to use `string.Join()` for simplicity (and for the "don't reinvent the wheel" principle) – digEmAll Feb 04 '11 at 08:57
  • 1
    @digEmAll: But the basics are still corecct, so there is no need for excuses :-). The string.Join(string, string[]) and string.Join(string, string[], int, int) overloads are still use FastAllocateString and thus maybe faster than the other overloads of string.Join. But I agree to the preferable usage of string.Join rather than implementing own Join-Logic with a StringBuilder. – jb_ Feb 04 '11 at 09:38
4

If you're using .NET 4 you can use the overload for string.Join that takes an IEnumerable if you have them in a List, too:

string.Join(", ", strings);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jackson Pope
  • 14,520
  • 6
  • 56
  • 80
1

You could override your object's ToString() method:

public override string ToString ()
{
    return string.Format ("{0},{1},{2}", this.number, this.id, this.whatever);
}
Dimitris Tavlikos
  • 8,170
  • 1
  • 27
  • 31
  • This is only useful if he knows exactly the number of elements he want to put in the string however. string.Join is probably a better fit. – Øyvind Bråthen Feb 03 '11 at 09:06
  • Yes but since he wants to create a csv file, the elements will most likely be the same every time. I don't know, it's a matter of preference. – Dimitris Tavlikos Feb 03 '11 at 09:12
  • I assumed from the question that he always want the same fields so he definately does know the number of elements. Nothing wrong with this answer although I don't really like overriding ToString here as it may not be general purpose enough. +1 from me anyway. – fearofawhackplanet Feb 03 '11 at 09:16
  • @fearofawhackplanet - Nothing wrong with it. Just a matter of preference ;) – Øyvind Bråthen Feb 03 '11 at 09:21
0

Yes, there can be multiple ways to do this.

If you have List/Array of strings then you can adopt it;

string[] myStrings = new string[] { "Hi", "stackoverflow", };
string csvString = string.Join(",", myStrings);  // csvString :: Hi,stackoverflow

If you have multiple strings then you can adopt it;

string st1 = "Hi";
string st2 = "stackoverflow";
string st3 = "team";
string csvString = string.Join(",", st1, st2, st3);  // csvString :: Hi,stackoverflow,team
Arsman Ahmad
  • 2,000
  • 1
  • 26
  • 34