ejemplo - convertir excel a csv separado por punto y coma




Tratar con comas en un archivo CSV (15)

Agregue una referencia a Microsoft.VisualBasic (sí, dice VisualBasic pero también funciona en C #, recuerde que al final solo es IL).

Use la clase Microsoft.VisualBasic.FileIO.TextFieldParser para analizar el archivo CSV. Aquí está el código de ejemplo:

 Dim parser As TextFieldParser = New TextFieldParser("C:\mar0112.csv")
 parser.TextFieldType = FieldType.Delimited
 parser.SetDelimiters(",")      

   While Not parser.EndOfData         
      'Processing row             
      Dim fields() As String = parser.ReadFields         
      For Each field As String In fields             
         'TODO: Process field                   

      Next      
      parser.Close()
   End While 

Estoy buscando sugerencias sobre cómo manejar un archivo csv que se crea, que luego suben nuestros clientes y que puede tener una coma en un valor, como el nombre de una empresa.

Algunas de las ideas que estamos analizando son: Identificadores citados (valor "," valores "," etc.) o usar una | en lugar de una coma. El mayor problema es que tenemos que hacerlo fácil, o el cliente no lo hará.


Como han dicho otros, es necesario escapar de los valores que incluyen citas. Aquí hay un pequeño lector CSV en C♯ que admite valores citados, incluidas las cotizaciones incrustadas y retornos de carro.

Por cierto, este es un código probado por unidades. Lo estoy publicando ahora porque esta pregunta parece surgir mucho y es posible que otros no quieran una biblioteca completa cuando el simple soporte de CSV funcionará.

Puedes usarlo de la siguiente manera:

using System;
public class test
{
    public static void Main()
    {
        using ( CsvReader reader = new CsvReader( "data.csv" ) )
        {
            foreach( string[] values in reader.RowEnumerator )
            {
                Console.WriteLine( "Row {0} has {1} values.", reader.RowIndex, values.Length );
            }
        }
        Console.ReadLine();
    }
}

Aquí están las clases. Tenga en cuenta que también puede usar la función Csv.Escape para escribir un CSV válido.

using System.IO;
using System.Text.RegularExpressions;

public sealed class CsvReader : System.IDisposable
{
    public CsvReader( string fileName ) : this( new FileStream( fileName, FileMode.Open, FileAccess.Read ) )
    {
    }

    public CsvReader( Stream stream )
    {
        __reader = new StreamReader( stream );
    }

    public System.Collections.IEnumerable RowEnumerator
    {
        get {
            if ( null == __reader )
                throw new System.ApplicationException( "I can't start reading without CSV input." );

            __rowno = 0;
            string sLine;
            string sNextLine;

            while ( null != ( sLine = __reader.ReadLine() ) )
            {
                while ( rexRunOnLine.IsMatch( sLine ) && null != ( sNextLine = __reader.ReadLine() ) )
                    sLine += "\n" + sNextLine;

                __rowno++;
                string[] values = rexCsvSplitter.Split( sLine );

                for ( int i = 0; i < values.Length; i++ )
                    values[i] = Csv.Unescape( values[i] );

                yield return values;
            }

            __reader.Close();
        }
    }

    public long RowIndex { get { return __rowno; } }

    public void Dispose()
    {
        if ( null != __reader ) __reader.Dispose();
    }

    //============================================


    private long __rowno = 0;
    private TextReader __reader;
    private static Regex rexCsvSplitter = new Regex( @",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))" );
    private static Regex rexRunOnLine = new Regex( @"^[^""]*(?:""[^""]*""[^""]*)*""[^""]*$" );
}

public static class Csv
{
    public static string Escape( string s )
    {
        if ( s.Contains( QUOTE ) )
            s = s.Replace( QUOTE, ESCAPED_QUOTE );

        if ( s.IndexOfAny( CHARACTERS_THAT_MUST_BE_QUOTED ) > -1 )
            s = QUOTE + s + QUOTE;

        return s;
    }

    public static string Unescape( string s )
    {
        if ( s.StartsWith( QUOTE ) && s.EndsWith( QUOTE ) )
        {
            s = s.Substring( 1, s.Length - 2 );

            if ( s.Contains( ESCAPED_QUOTE ) )
                s = s.Replace( ESCAPED_QUOTE, QUOTE );
        }

        return s;
    }


    private const string QUOTE = "\"";
    private const string ESCAPED_QUOTE = "\"\"";
    private static char[] CHARACTERS_THAT_MUST_BE_QUOTED = { ',', '"', '\n' };
}

Como se trata de prácticas generales, comencemos por las reglas generales:

  1. No use CSV, use XML con una biblioteca para leer y escribir el archivo xml en su lugar.

  2. Si debe utilizar CSV. Hágalo correctamente y use una biblioteca gratuita para analizar y almacenar los archivos CSV.

Para justificar 1), la mayoría de los analizadores CSV no son conscientes de la codificación, por lo que si no está tratando con US-ASCII está solicitando problemas. Por ejemplo, excel 2002 es almacenar el CSV en codificación local sin ninguna nota sobre la codificación. El estándar CSV no se adopta ampliamente :(. Por otro lado, el estándar xml está bien adoptado y maneja las codificaciones bastante bien.

Para justificar 2), hay un montón de analizadores csv para casi todos los idiomas, por lo que no hay necesidad de reinventar la rueda, incluso si las soluciones parecen bastante simples.

Para nombrar algunos:

  • para uso en python construir en modulo csv

  • para perl cheque CPAN y Text::CSV

  • para php use build en funciones fgetcsv / fputcsv

  • para la biblioteca SuperCVS cheque java

Realmente no hay necesidad de implementar esto a mano si no va a analizarlo en un dispositivo integrado.


Creo que la solución más fácil para este problema es hacer que el cliente abra el csv en Excel, y luego ctrl + r para reemplazar todas las comas con el identificador que desee. Esto es muy fácil para el cliente y requiere solo un cambio en su código para leer el delimitador de su elección.


En Europa tenemos este problema antes que esta pregunta. En Europa usamos toda una coma para un punto decimal. Vea estos números a continuación:

| American      | Europe        |
| ------------- | ------------- |
| 0.5           | 0,5           |
| 3.14159265359 | 3,14159265359 |
| 17.54         | 17,54         |
| 175,186.15    | 175.186,15    |

Por lo tanto, no es posible utilizar el separador de coma para archivos CSV. Por ese motivo, los archivos CSV en Europa están separados por un punto y coma ( ; ) .

Programas como Microsoft Excel pueden leer archivos con un punto y coma y es posible cambiar de separador. Incluso podrías usar una pestaña ( \t ) como separador. Vea esta respuesta de Supper User .


En caso de que esté en un sistema * nix , tenga acceso a sed y puede haber una o más comas no deseadas solo en un campo específico de su CSV, puede usar la siguiente frase para encerrarlas en " as RFC4180 Sección 2 propone:

sed -r 's/([^,]*,[^,]*,[^,]*,)(.*)(,.*,.*)/\1"\2"\3/' inputfile

Según el campo en el que se encuentren las comas no deseadas, debe modificar / ampliar los grupos de captura de la expresión regular (y la sustitución).
El ejemplo anterior incluirá el cuarto campo (de seis) entre comillas.

En combinación con la --in-place-option puede aplicar estos cambios directamente al archivo.

Para "construir" la expresión regular correcta, hay un principio simple a seguir:

  1. Para cada campo en su CSV que viene antes del campo con la (s) coma (s) no deseada (s), escriba un [^,]*, y póngalas todas juntas en un grupo de captura.
  2. Para el campo que contiene las comas no deseadas, escriba (.*) .
  3. Para cada campo después del campo con la (s) coma (s) no deseada (s) escriba uno,. ,.* Y póngalos todos juntos en un grupo de captura.

Aquí hay una breve descripción de diferentes posibles expresiones regulares / sustituciones dependiendo del campo específico. Si no se da, la sustitución es \1"\2"\3 .

([^,]*)(,.*)                     #first field, regex
"\1"\2                           #first field, substitution

(.*,)([^,]*)                     #last field, regex
\1"\2"                           #last field, substitution


([^,]*,)(.*)(,.*,.*,.*)          #second field (out of five fields)
([^,]*,[^,]*,)(.*)(,.*)          #third field (out of four fields)
([^,]*,[^,]*,[^,]*,)(.*)(,.*,.*) #fourth field (out of six fields)

Si desea eliminar las comas no deseadas con sed lugar de incluirlas entre comillas, consulte esta respuesta .


Hay una biblioteca disponible a través de nuget para tratar casi cualquier CSV bien formado (.net) - CsvHelper

Ejemplo para mapear a una clase:

var csv = new CsvReader( textReader );
var records = csv.GetRecords<MyClass>();

Ejemplo para leer campos individuales:

var csv = new CsvReader( textReader );
while( csv.Read() )
{
    var intField = csv.GetField<int>( 0 );
    var stringField = csv.GetField<string>( 1 );
    var boolField = csv.GetField<bool>( "HeaderName" );
}

Dejando que el cliente maneje el formato de archivo:
, es el delimitador de campo estándar, " es el valor estándar utilizado para escapar de los campos que contienen un delimitador, una comilla o un final de línea.

Para usar (por ejemplo) # para campos y ' para escapar:

var csv = new CsvReader( textReader );
csv.Configuration.Delimiter = "#";
csv.Configuration.Quote = ''';
// read the file however meets your needs

Más documentación


La solución más sencilla que he encontrado es la que utiliza LibreOffice:

  1. Reemplazar todo literal " por
  2. Pon comillas dobles alrededor de tu cadena

También puedes usar el que usa Excel:

  1. Reemplazar todo literal " por ""
  2. Pon comillas dobles alrededor de tu cadena

Tenga en cuenta que otras personas recomendaron hacer solo el paso 2 anterior, pero eso no funciona con líneas en las que a " va seguido de a, como en un CSV donde desea tener una sola columna con la cadena hello",world , como CSV diría:

"hello",world"

Lo que se interpreta como una fila con dos columnas: hello y world"


Pon comillas dobles alrededor de las cuerdas. Eso es generalmente here .

Ala eli

Usted se escapa de una cita doble como dos comillas dobles. Por ejemplo, "test1", "foo" "barra", "test2"


Primero, preguntémonos: "¿Por qué sentimos la necesidad de manejar las comas de manera diferente para los archivos CSV?"

Para mí, la respuesta es: "Porque cuando exporto datos a un archivo CSV, las comas en un campo desaparecen y mi campo se separa en varios campos donde las comas aparecen en los datos originales". (Eso es porque la coma es el carácter separador de campo CSV.)

Dependiendo de su situación, los puntos y coma también se pueden usar como separadores de campo CSV.

Dados mis requisitos, puedo usar un carácter, por ejemplo, una comilla simple de menos de 9, que parece una coma.

Así que, así es como puedes hacerlo en Go:

// Replace special CSV characters with single low-9 quotation mark
func Scrub(a interface{}) string {
    s := fmt.Sprint(a)
    s = strings.Replace(s, ",", "‚", -1)
    s = strings.Replace(s, ";", "‚", -1)
    return s
}

El segundo carácter de coma en la función Reemplazar es decimal 8218.

Tenga en cuenta que si tiene clientes que pueden tener lectores de texto solo ASCII, este carácter de Décimo 8218 no se verá como una coma. Si este es su caso, entonces recomiendo rodear el campo con la coma (o el punto y coma) con comillas dobles según RFC 4128: https://tools.ietf.org/html/rfc4180


Puedes leer el archivo csv como este.

Esto hace uso de splits y cuida espacios.

ArrayList List = new ArrayList();
static ServerSocket Server;
static Socket socket;
static ArrayList<Object> list = new ArrayList<Object>();


public static void ReadFromXcel() throws FileNotFoundException
{   
    File f = new File("Book.csv");
    Scanner in = new Scanner(f);
    int count  =0;
    String[] date;
    String[] name;
    String[] Temp = new String[10];
    String[] Temp2 = new String[10];
    String[] numbers;
    ArrayList<String[]> List = new ArrayList<String[]>();
    HashMap m = new HashMap();

         in.nextLine();
         date = in.nextLine().split(",");
         name = in.nextLine().split(",");
         numbers = in.nextLine().split(",");
         while(in.hasNext())
         {
             String[] one = in.nextLine().split(",");
             List.add(one);
         }
         int xount = 0;
         //Making sure the lines don't start with a blank
         for(int y = 0; y<= date.length-1; y++)
         {
             if(!date[y].equals(""))
             {   
                 Temp[xount] = date[y];
                 Temp2[xount] = name[y];
                 xount++;
             }
         }

         date = Temp;
         name =Temp2;
         int counter = 0;
         while(counter < List.size())
         {
             String[] list = List.get(counter);
             String sNo = list[0];
             String Surname = list[1];
             String Name = list[2];
             for(int x = 3; x < list.length; x++)
             {           
                 m.put(numbers[x], list[x]);
             }
            Object newOne = new newOne(sNo, Name, Surname, m, false);
             StudentList.add(s);
             System.out.println(s.sNo);
             counter++;
         }

Puedes poner comillas dobles alrededor de los campos. No me gusta este enfoque, ya que agrega otro carácter especial (la doble cita). Solo define un carácter de escape (generalmente barra diagonal inversa) y úsalo donde necesites escapar de algo:

data,more data,more data\, even,yet more

No tiene que intentar hacer coincidir las comillas, y tiene menos excepciones para analizar. Esto simplifica tu código, también.


Si tiene ganas de reinventar la rueda, lo siguiente puede funcionar para usted:

public static IEnumerable<string> SplitCSV(string line)
{
    var s = new StringBuilder();
    bool escaped = false, inQuotes = false;
    foreach (char c in line)
    {
        if (c == ',' && !inQuotes)
        {
            yield return s.ToString();
            s.Clear();
        }
        else if (c == '\\' && !escaped)
        {
            escaped = true;
        }
        else if (c == '"' && !escaped)
        {
            inQuotes = !inQuotes;
        }
        else
        {
            escaped = false;
            s.Append(c);
        }
    }
    yield return s.ToString();
}

Usualmente hago esto en mis rutinas de análisis de archivos CSV. Suponga que la variable 'línea' es una línea dentro de un archivo CSV y que todos los valores de las columnas están entre comillas dobles. Después de que se ejecuten las dos líneas siguientes, obtendrá columnas CSV en la colección 'valores'.

// The below two lines will split the columns as well as trim the DBOULE QUOTES around values but NOT within them
    string trimmedLine = line.Trim(new char[] { '\"' });
    List<string> values = trimmedLine.Split(new string[] { "\",\"" }, StringSplitOptions.None).ToList();

    public static IEnumerable<string> LineSplitter(this string line, char 
         separator, char skip = '"')
    {
        var fieldStart = 0;
        for (var i = 0; i < line.Length; i++)
        {
            if (line[i] == separator)
            {
                yield return line.Substring(fieldStart, i - fieldStart);
                fieldStart = i + 1;
            }
            else if (i == line.Length - 1)
            {
                yield return line.Substring(fieldStart, i - fieldStart + 1);
                fieldStart = i + 1;
            }

            if (line[i] == '"')
                for (i++; i < line.Length && line[i] != skip; i++) { }
        }

        if (line[line.Length - 1] == separator)
        {
            yield return string.Empty;
        }
    }




csv