Al leer un archivo CSV usando un DataReader y el proveedor de datos OLEDB Jet, ¿cómo puedo controlar los tipos de datos de las columnas?

StackOverflow https://stackoverflow.com/questions/115658

  •  02-07-2019
  •  | 
  •  

Pregunta

En mi aplicación C# estoy usando el proveedor de datos Microsoft Jet OLEDB para leer un archivo CSV.La cadena de conexión se ve así:

Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Data;Extended Properties="text;HDR=Yes;FMT=Delimited

Abro una ADO.NET OleDbConnection usando esa cadena de conexión y selecciono todas las filas del archivo CSV con el comando:

select * from Data.csv

Cuando abro un OleDbDataReader y examino los tipos de datos de las columnas que devuelve, encuentro que algo en la pila ha intentado adivinar los tipos de datos basándose en la primera fila de datos del archivo.Por ejemplo, supongamos que el archivo CSV contiene:

House,Street,Town
123,Fake Street,Springfield
12a,Evergreen Terrace,Springfield

Llamar al método OleDbDataReader.GetDataTypeName para la columna House revelará que a la columna se le ha asignado el tipo de datos "DBTYPE_I4", por lo que todos los valores leídos en ella se interpretan como números enteros.Mi problema es que House debería ser una cadena: cuando intento leer el valor de House de la segunda fila, OleDbDataReader devuelve nulo.

¿Cómo puedo decirle al proveedor de la base de datos Jet o al OleDbDataReader que interprete una columna como cadenas en lugar de números?

¿Fue útil?

Solución

Hay un archivo de esquema que puede crear y que le indicaría a ADO.NET cómo interpretar el CSV, dándole de hecho una estructura.

Prueba esto: http://www.aspdotnetcodes.com/Importing_CSV_Database_Schema.ini.aspx

Otros consejos

Para ampliar la respuesta de Marc, necesito crear un archivo de texto llamado Schema.ini y colocarlo en el mismo directorio que el archivo CSV.Además de los tipos de columnas, este archivo puede especificar el formato de archivo, el formato de fecha y hora, la configuración regional y los nombres de las columnas si no están incluidos en el archivo.

Para que el ejemplo que di en la pregunta funcione, el archivo de esquema debería verse así:

[Data.csv]
ColNameHeader=True
Col1=House Text
Col2=Street Text
Col3=Town Text

También podría intentar esto para que el proveedor de datos examine todas las filas del archivo antes de intentar adivinar los tipos de datos:

[Data.csv]
ColNameHeader=true
MaxScanRows=0

En la vida real, mi aplicación importa datos de archivos con nombres dinámicos, por lo que tengo que crear un archivo Schema.ini sobre la marcha y escribirlo en el mismo directorio que el archivo CSV antes de abrir mi conexión.

Más detalles pueden ser encontrados aquí - http://msdn.microsoft.com/en-us/library/ms709353(VS.85).aspx - o buscando en la biblioteca MSDN el "archivo Schema.ini".

por favor, compruebe

http://kbcsv.codeplex.com/

using (var reader = new CsvReader("data.csv"))
{
    reader.ReadHeaderRecord();
    foreach (var record in reader.DataRecords)
    {
        var name = record["Name"];
        var age = record["Age"];
    }
}

Debe indicarle al controlador que escanee todas las filas para determinar el esquema.De lo contrario, si las primeras filas son numéricas y el resto son alfanuméricas, las celdas alfanuméricas estarán en blanco.

Como rory, descubrí que necesitaba crear un archivo esquema.ini dinámicamente porque no hay forma de indicarle mediante programación al controlador que escanee todas las filas.(este no es el caso de los archivos de Excel)

Debes tener MaxScanRows=0 en tu esquema.ini

Aquí hay un ejemplo de código:

    public static DataTable GetDataFromCsvFile(string filePath, bool isFirstRowHeader = true)
    {
        if (!File.Exists(filePath))
        {
            throw new FileNotFoundException("The path: " + filePath + " doesn't exist!");
        }

        if (!(Path.GetExtension(filePath) ?? string.Empty).ToUpper().Equals(".CSV"))
        {
            throw new ArgumentException("Only CSV files are supported");
        }
        var pathOnly = Path.GetDirectoryName(filePath);
        var filename = Path.GetFileName(filePath);
        var schemaIni =
            $"[{filename}]{Environment.NewLine}" +
            $"Format=CSVDelimited{Environment.NewLine}" +
            $"ColNameHeader={(isFirstRowHeader ? "True" : "False")}{Environment.NewLine}" +
            $"MaxScanRows=0{Environment.NewLine}" +
            $" ; scan all rows for data type{Environment.NewLine}" +
            $" ; This file was automatically generated";
        var schemaFile = pathOnly != null ? Path.Combine(pathOnly, "schema.ini") : "schema.ini";
        File.WriteAllText(schemaFile, schemaIni);

        try
        {
            var sqlCommand = $@"SELECT * FROM [{filename}]";

            var oleDbConnString =
                $"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={pathOnly};Extended Properties=\"Text;HDR={(isFirstRowHeader ? "Yes" : "No")}\"";

            using (var oleDbConnection = new OleDbConnection(oleDbConnString))
            using (var adapter = new OleDbDataAdapter(sqlCommand, oleDbConnection))
            using (var dataTable = new DataTable())
            {
                adapter.FillSchema(dataTable, SchemaType.Source);
                adapter.Fill(dataTable);
                return dataTable;
            }
        }
        finally
        {
            if (File.Exists(schemaFile))
            {
                File.Delete(schemaFile);
            }
        }
    }

Deberá realizar algunas modificaciones si ejecuta esto en el mismo directorio en varios subprocesos al mismo tiempo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top