DISABILITARE ADBLOCK

ADBlock sta bloccando alcuni contenuti del sito

ADBlock errore
risultati trovati: 

DOMANDA

Modo tipico di creare una CSV (pseudocodice):

  1. Crea un oggetto contenitore CSV (come StringBuilder in C #).
  2. Scorri le stringhe che desideri aggiungere aggiungendo una virgola dopo ciascuna.
  3. Dopo il ciclo, rimuovi l'ultima virgola superflua.

Esempio di codice:

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

Mi piace l'idea di aggiungere la virgola controllando se il contenitore è vuoto, ma ciò non significa più elaborazione in quanto deve controllare la lunghezza della stringa in ogni occorrenza?

Sento che dovrebbe esserci un modo più semplice / più pulito / più efficiente per rimuovere l'ultima virgola. Qualche idea?

SOLUZIONE

Puoi utilizzare LINQ to Objects :

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

Ovviamente ciò potrebbe essere fatto tutto in una riga, ma è un po 'più chiaro in due.

Se ti va lasciaci una tua opinione

L'articolo ti è stato utile ed è tradotto correttamente?

ALTRI SUGGERIMENTI

Il tuo codice non è veramente conforme al formato CSV completo . Se stai solo generando CSV da dati che non hanno virgole, spazi iniziali / finali, tabulazioni, nuove righe o virgolette, dovrebbe andare bene. Tuttavia, nella maggior parte degli scenari di scambio di dati nel mondo reale, è necessaria la piena implementazione.

Per la generazione al CSV corretto, puoi usare questo:

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

Potrebbe non essere il codice più efficiente del mondo, ma è stato testato. Il mondo reale fa schifo rispetto al codice rapido di esempio :)

Perché non utilizzare una delle librerie CSV open source là fuori?

So che sembra eccessivo per qualcosa che sembra così semplice, ma come puoi dire dai commenti e dagli snippet di codice, c'è molto di più di quello che si vede. Oltre a gestire la piena conformità CSV, alla fine ti consigliamo di gestire sia la lettura che la scrittura di CSV ... e potresti voler manipolare i file.

Ho usato Apri CSV su uno dei miei progetti prima (ma ce ne sono molti altri tra cui scegliere a partire dal). Certamente mi ha semplificato la vita. ;)

Non dimenticare il nostro vecchio amico " per " ;. Non è bello come foreach ma ha il vantaggio di poter iniziare dal secondo elemento.

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

Puoi anche racchiudere la seconda Append in un " if " che verifica se la proprietà Name contiene una virgoletta doppia o una virgola e, in caso affermativo, evita in modo appropriato.

Puoi invece aggiungere la virgola come prima cosa all'interno di foreach.

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

Puoi anche creare un array di c.Name e utilizzare il metodo String.Join per creare la tua linea.

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

Potrebbe non essere altrettanto efficace dell'approccio StringBuilder , ma sembra decisamente più pulito.

Inoltre, potresti prendere in considerazione l'utilizzo di .CurrentCulture.TextInfo.ListSeparator invece di una virgola codificata: se l'output verrà importato in altre applicazioni, potresti avere problemi con esso. ListSeparator può variare in base alle diverse culture e, per lo meno, MS Excel rispetta questa impostazione. Quindi:

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

  

Mi piace l'idea di aggiungere la virgola controllando se il contenitore è vuoto, ma ciò non significa più elaborazione in quanto deve controllare la lunghezza della stringa in ogni occorrenza?

Stai ottimizzando prematuramente, il colpo di prestazione sarebbe trascurabile.

Solo un pensiero, ma ricordati di gestire virgole e virgolette (") nei valori dei campi, altrimenti il ??tuo file CSV potrebbe interrompere il lettore dei consumatori.

Ho scritto una piccola lezione per questo nel caso in cui qualcun altro lo ritenga utile ...

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

Ho usato questo metodo prima. La proprietà Lunghezza di StringBuilder NON è di sola lettura, quindi sottraendola in un modo tronca l'ultimo carattere. Ma devi assicurarti che la tua lunghezza non sia zero per cominciare (cosa che succederebbe se la tua lista fosse vuota) perché impostare la lunghezza a meno di zero è un errore.

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

Uso CSVHelper - è una grande libreria open source che ti consente di generare flussi CSV conformi un elemento alla volta o mappa personalizzata le tue lezioni:

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

o se si mappa quindi qualcosa del genere: csvWriter.WriteRecords<ContactList>(contactList);

Che ne dici di un po 'di taglio?

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

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

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

Che ne dici di rintracciare se sei sul primo elemento e aggiungere una virgola prima dell'articolo se non è il primo.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow