Vra

Tipiese manier van die skep van 'n CSV string (pseudokode):

  1. Skep 'n CSV houer voorwerp (soos 'n StringBuilder in C #).
  2. Loop deur die snare wat jy wil byvoeg aanbring n komma na elkeen.
  3. Na die lus, verwyder wat verlede oorbodig komma.

Kode monster:

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

Ek hou van die idee van die toevoeging van die komma deur seker te maak dat die houer leeg is, maar nie wat beteken dat meer verwerking as wat dit nodig het om die lengte van die string te kyk na elke gebeurtenis?

Ek voel dat daar 'n makliker / skoonmaker / meer doeltreffende manier van die verwydering van die laaste komma moet wees. Enige idees?

Was dit nuttig?

Oplossing

Jy kan gebruik LINQ om voorwerpe :

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

Dit is duidelik dat dit kan al gedoen in een lyn, maar dit is 'n bietjie duideliker op twee.

Ander wenke

Jou kode nie regtig voldoen aan volle CSV formaat . As jy net die opwekking van CSV van data wat geen kommas het, voorste / sleep spasies, oortjies, newlines of aanhalings, moet dit goed wees. Maar in die meeste werklike wêreld data-uitruiling scenario, het jy nodig die volle imlementation.

Vir geslag tot behoorlike CSV, kan jy dit gebruik:

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;
}

Kan nie wêreld se mees doeltreffende kode wees, maar dit is getoets. Werklike wêreld suig in vergelyking met 'n vinnige voorbeeld kode:)

Hoekom nie gebruik maak van een van die open source CSV biblioteke daar buite?

Ek weet dit klink soos overkill vir iets wat so eenvoudig lyk, maar as jy kan vertel deur die kommentaar en stukkies kode, daar is meer as wat die oog. In bykomend tot die hantering van volle CSV nakoming, sal jy uiteindelik wil beide lees en skryf CSVs ... hanteer en wil jy dalk lêer manipulasie.

Ek het gebruik Open CSV op een van my projekte voor (maar daar is baie van die ander te kies van). Dit is beslis gemaak my lewe makliker te maak. ;)

Moenie vergeet ons ou vriend "vir". Dit is nie so lekker-kyk as foreach maar dit het die voordeel dat dit in staat is om te begin op die tweede element.

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

Jy kan ook die tweede Append draai in 'n "as" wat toetse of die eiendom Naam bevat 'n dubbel-aanhaling of 'n komma, en indien wel, ontsnap hulle gepas.

Jy kan in plaas voeg die komma as die eerste ding in jou foreach.

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

Jy kan ook 'n verskeidenheid van c.Name data en gebruik String.Join metode om jou lyn te skep.

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

Dit kan nie so performante as die StringBuilder benadering wees, maar dit lyk beslis skoner.

Ook, wil jy dalk oorweeg om .CurrentCulture.TextInfo.ListSeparator in plaas van 'n harde-gekodeerde komma - As jou uitset gaan in ander programme ingevoer word, kan jy probleme met Dit. ListSeparator kan verskillende in verskillende kulture te wees, en MS Excel op die heel minste, honneurs hierdie instelling. So:

return String.Join(
    System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator,
    tmpList.ToArray());
  

Ek hou van die idee van die toevoeging van die komma deur seker te maak dat die houer leeg is, maar nie wat beteken dat meer verwerking as wat dit nodig het om die lengte van die string te kyk na elke gebeurtenis?

Jy voortydig optimalisering, die prestasie treffer sou gering wees.

Net 'n gedagte, maar onthou om kommas en aanhalingstekens ( ") te hanteer in die veld waardes, anders sal jou CSV kan die verbruikers leser breek.

Ek het 'n klein klas vir hierdie in geval iemand anders vind dit nuttig ...

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

Ek het hierdie metode voor gebruik. Die lengte eiendom van StringBuilder is NIE leesalleen so trek dit deur een beteken afgestomp die laaste karakter. Maar jy het om seker te maak jou lengte is nie nul te begin met (wat sou gebeur as jou lys is leeg) omdat die opstel van die lengte tot minder as nul is 'n fout.

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

Ek gebruik CSVHelper - dit is 'n groot open-source biblioteek wat kan jy genereer voldoen CSV strome een element op 'n tyd of persoonlike-kaart om jou klasse:

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

of as jy die kaart dan iets soos hierdie: csvWriter.WriteRecords<ContactList>(contactList);

Hoe gaan dit met 'n paar snoei?

public string ReturnAsCSV(ContactList contactList)
{
    StringBuilder sb = new StringBuilder();

    foreach (Contact c in contactList)
    {
        sb.Append(c.Name + ",");
    }

    return sb.ToString().Trim(',');
}

Hoe gaan dit met die dop of jy op die eerste item, en voeg net 'n komma voor die item as dit is nie die eerste een.

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();
}
Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top