Create a comma-separated strings in C#
Asked Answered
E

6

73

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?

Enyedy answered 3/2, 2011 at 9:0 Comment(1)
Generally speaking it's considered less efficient to concatenate strings with the + symbol. It creates more objects to be garbage collected.Wernher
C
145

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, ...);
Curlpaper answered 3/2, 2011 at 9:3 Comment(9)
+1 for using an array in between which keeps the code more legible when there are more than "a few" values.Allanite
@Allanite - 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.Argyres
Why two downvotes on this answer? A reason would be appreciated so everyone knows what you feel are the problem with this answer.Argyres
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).Everara
@Everara - 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 ;)Argyres
+1 for using an array; it's neater and functionality can be extended more easily.Wernher
@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.Johnjohna
This is not strictly the correct answer for CSV strings. True CSV will escape strings as needed.Elaterium
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.Flinty
T
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
Tillage answered 3/2, 2011 at 10:29 Comment(0)
A
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.

Aventurine answered 3/2, 2011 at 9:4 Comment(6)
string.Join is as fast as StringBuilder (if not slightly faster) because they both allocate one string onlyOlivas
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.Argyres
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<string>... anyway .NET 4.0 has fixed this problemOlivas
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?Aventurine
@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)Olivas
@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.Aventurine
P
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);
Poppycock answered 3/2, 2011 at 9:6 Comment(0)
S
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);
}
Sotelo answered 3/2, 2011 at 9:4 Comment(4)
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.Argyres
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.Sotelo
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.Everara
@Everara - Nothing wrong with it. Just a matter of preference ;)Argyres
F
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
Friable answered 20/2, 2022 at 19:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.