quitar - string c# ejemplos
Manejo de cadenas CSV (14)
Me gusta la idea de agregar la coma al verificar si el contenedor está vacío, pero ¿no significa eso que necesita más procesamiento ya que necesita verificar la longitud de la cadena en cada aparición?
Estás optimizando prematuramente, el rendimiento alcanzado sería insignificante.
Forma típica de crear una cadena CSV (pseudocódigo):
- Cree un objeto contenedor CSV (como un StringBuilder en C #).
- Pasa por las cuerdas que quieras agregar y añades una coma después de cada una.
- Después del ciclo, elimina esa última coma superflua.
Muestra 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 agregar la coma al verificar si el contenedor está vacío, pero ¿no significa eso que necesita más procesamiento ya que necesita verificar la longitud de la cadena en cada aparición?
Siento que debería haber una manera más fácil / limpia / más eficiente de eliminar esa última coma. ¿Algunas ideas?
Solo un pensamiento, pero recuerde manejar las comas y las comillas (") en los valores de campo, de lo contrario su archivo CSV puede romper el lector del consumidor.
Lo siento, ejemplo específico de PHP, pero podría ayudar a alguien.
¿Por qué no utilizar una de las bibliotecas de código abierto CSV que hay?
Sé que suena excesivo para algo que parece tan simple, pero como puedes ver por los comentarios y fragmentos de código, hay más de lo que parece. Además de manejar el cumplimiento completo de CSV, eventualmente querrá manejar CSV de lectura y escritura ... y es posible que desee la manipulación de archivos.
He utilizado Open CSV en uno de mis proyectos antes (pero hay muchos otros para elegir). Ciertamente hizo mi vida más fácil. ;)
Puedes usar LINQ to Objects :
string [] strings = contactList.Select(c => c.Name).ToArray();
string csv = string.Join(",", strings);
Obviamente, eso podría hacerse en una sola línea, pero es un poco más claro en dos.
En su lugar, podría agregar la coma como la primera cosa dentro de su foreach.
if (sb.Length > 0) sb.Append(",");
Escribí una clase pequeña para esto en caso de que alguien más lo encuentre ú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());
}
}
}
Uso CSVHelper : es una gran biblioteca de código abierto que te permite generar flujos de CSV compatibles de un elemento a la vez o personalizar tus 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 csvWriter.WriteRecords<ContactList>(contactList);
entonces algo como esto: csvWriter.WriteRecords<ContactList>(contactList);
¿Qué le parece seguir si está en el primer artículo y solo agrega una coma antes del artículo si no es el primero?
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();
}
¿Qué tal un poco de recorte?
public string ReturnAsCSV(ContactList contactList)
{
StringBuilder sb = new StringBuilder();
foreach (Contact c in contactList)
{
sb.Append(c.Name + ",");
}
return sb.ToString().Trim('','');
}
He usado este método antes La propiedad Length de StringBuilder NO es de solo lectura, por lo que al restarla se truncan el último carácter. Pero debe asegurarse de que su longitud no sea cero para empezar (lo que sucedería si su lista está vacía) porque establecer la longitud en menos de 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();
}
No olvides a nuestro viejo amigo "por". No es tan atractivo como foreach, pero tiene la ventaja de poder 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();
}
También puede ajustar el segundo Append en un "si" que prueba si la propiedad Name contiene una comilla doble o una coma, y si es así, debe escapar de manera apropiada.
Su código no es realmente compatible con el formato CSV completo . Si solo está generando CSV a partir de datos que no tienen comas, espacios iniciales / finales, pestañas, nuevas líneas o comillas, debería estar bien. Sin embargo, en la mayoría de los escenarios de intercambio de datos del mundo real, necesita la implementación completa.
Para generar un CSV adecuado, puede usar 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;
}
Puede que no sea el código más eficiente del mundo, pero ha sido probado. El mundo real apesta comparado con el código de muestra rápido :)
También puede crear una matriz de datos c.Name y usar el método String.Join 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 enfoque StringBuilder , pero definitivamente parece más limpio.
Además, es posible que desee considerar usar .CurrentCulture.TextInfo.ListSeparator en lugar de una coma codificada: si su salida se importará a otras aplicaciones, es posible que tenga problemas con ella. ListSeparator puede ser diferente en diferentes culturas, y MS Excel por lo menos, respeta esta configuración. Asi que:
return String.Join(
System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator,
tmpList.ToArray());