Pregunta

Forma típica de la creación de un CSV cadena (pseudocódigo):

  1. Crear un CSV contenedor de objeto (como un StringBuilder en C#).
  2. El bucle a través de las cadenas desea agregar añadiendo una coma después de cada uno.
  3. Después de que el bucle se remueve el último superfluo coma.

Ejemplo de código:

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

Me gusta la idea de añadir a la coma, mediante la comprobación de si el contenedor está vacío, pero no significa eso que más procesamiento que necesita para comprobar la longitud de la cadena en cada aparición?

Creo que debe haber una manera más fácil/limpiador/forma más eficiente de eliminar que la última coma.Alguna idea?

¿Fue útil?

Solución

Usted podría utilizar LINQ to Objects:

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

Obviamente que todo podría ser hecho en una línea, pero es un poco más clara en dos.

Otros consejos

El código no es realmente compatible con completa el formato CSV.Si se acaba de generar CSV a partir de datos que no tiene comas, líder/espacios, tabulaciones, saltos de línea o cotizaciones, que debería estar bien.Sin embargo, en la mayoría de los datos del mundo real de intercambio de los casos, usted no necesita la plena realización.

Para la generación correcta de CSV, puede utilizar esto:

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

No podría ser más eficientes del mundo de código, pero no ha sido probado.Mundo Real chupa comparación rápida de código de ejemplo :)

¿Por qué no usar uno de los de código abierto CSV bibliotecas ahí fuera?

Sé que suena como una exageración de algo que parece tan simple, pero como se puede ver por los comentarios y los fragmentos de código, hay más de lo que parece a simple vista.Además de la manipulación completa CSV cumplimiento, que finalmente va a querer manejar la lectura y la escritura CSVs...y es posible que desee manipulación de archivos.

He usado Abiertos CSV en uno de mis proyectos antes (pero hay muchos otros a elegir).Ciertamente ha hecho mi vida más fácil.;)

No se olvide de nuestro viejo amigo "para".No es tan bonita como foreach pero tiene la ventaja de ser capaz de comenzar en el segundo 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();
}

Usted también podría envolver el segundo Anexar en un "si" que comprueba si el Nombre de la propiedad contiene una doble cita o una coma, y si es así, escapar de ellos apropiadamente.

En su lugar podría agregar la coma como el primero en el interior de su foreach.

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

Usted también podría hacer una matriz de c.Nombre los datos y el uso De la cadena.Únete método para crear su línea.

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

Esto podría no ser tan eficiente como el StringBuilder enfoque, pero definitivamente se ve más limpio.

También, usted puede ser que desee considerar el uso de .CurrentCulture.TextInfo.ListSeparator en lugar de una rígida coma, Si la salida va a ser importados en otras aplicaciones, es posible que tenga problemas con ella.ListSeparator puede ser diferente en diferentes culturas, y MS Excel, al menos, la honra de esta configuración.Así:

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

Me gusta la idea de añadir a la coma, mediante la comprobación de si el contenedor está vacío, pero no significa eso que más procesamiento que necesita para comprobar la longitud de la cadena en cada aparición?

Estás prematuramente la optimización, el impacto en el rendimiento sería insignificante.

Sólo un pensamiento, pero recuerde que debe manejar por comas y comillas ( "" ) en el campo de los valores, de lo contrario el archivo CSV puede romper los consumidores lector.

Escribí una pequeña clase para esto en caso de que alguien más lo encuentra útil...

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

He utilizado este método antes.La Longitud de la propiedad de StringBuilder NO es de sólo lectura para restar por uno de los medios truncar el último carácter.Pero usted tiene que asegurarse de que su longitud no es cero para empezar (que pasaría si la lista está vacía) debido a la fijación de la longitud de a es menor que cero, es un error.

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

Yo uso CSVHelper - es una gran fuente abierta de la biblioteca que te permite generar CSV compatible con secuencias de un elemento en un tiempo o la costumbre-mapa de las clases:

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 si usted mapa haga algo como esto: csvWriter.WriteRecords<ContactList>(contactList);

Cómo sobre algunos de recorte?

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

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

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

Cómo sobre el seguimiento de si usted está en el primer elemento, y sólo añadir una coma antes de el elemento si es que no la primera.

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();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top