Quando si legge un file CSV utilizzando un DataReader e il provider di dati Jet OLEDB, come posso controllare i tipi di dati delle colonne?

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

  •  02-07-2019
  •  | 
  •  

Domanda

Nella mia applicazione C # sto usando il provider di dati OLEDB di Microsoft Jet per leggere un file CSV. La stringa di connessione è simile alla seguente:

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

Apro un OleDbConnection ADO.NET usando quella stringa di connessione e seleziono tutte le righe dal file CSV con il comando:

select * from Data.csv

Quando apro un OleDbDataReader ed esamino i tipi di dati delle colonne che restituisce, trovo che qualcosa nello stack ha provato a indovinare i tipi di dati in base alla prima riga di dati nel file. Ad esempio, supponiamo che il file CSV contenga:

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

La chiamata al metodo OleDbDataReader.GetDataTypeName per la colonna House rivelerà che alla colonna è stato assegnato il tipo di dati "DBTYPE_I4", quindi tutti i valori letti da essa vengono interpretati come numeri interi. Il mio problema è che House dovrebbe essere una stringa: quando provo a leggere il valore House dalla seconda riga, OleDbDataReader restituisce null.

Come posso dire al provider di database Jet o al OleDbDataReader di interpretare una colonna come stringhe anziché numeri?

È stato utile?

Soluzione

C'è un file di schema che puoi creare che direbbe ad ADO.NET come interpretare il CSV - in effetti dandogli una struttura.

Prova questo: http://www.aspdotnetcodes.com/Importing_CSV_Database_Schema.ini.aspx

Altri suggerimenti

Per espandere la risposta di Marc, devo creare un file di testo chiamato Schema.ini e inserirlo nella stessa directory del file CSV. Oltre ai tipi di colonna, questo file può specificare il formato del file, il formato della data e dell'ora, le impostazioni internazionali e i nomi delle colonne se non sono inclusi nel file.

Per far funzionare l'esempio fornito nella domanda, il file Schema dovrebbe apparire così:

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

Potrei anche provare questo per fare in modo che il fornitore di dati esamini tutte le righe nel file prima di provare a indovinare i tipi di dati:

[Data.csv]
ColNameHeader=true
MaxScanRows=0

Nella vita reale, la mia applicazione importa i dati da file con nomi dinamici, quindi devo creare un file Schema.ini al volo e scriverlo nella stessa directory del file CSV prima di aprire la mia connessione.

Ulteriori dettagli sono disponibili qui - http: // msdn.microsoft.com/en-us/library/ms709353(VS.85).aspx - o cercando nella libreria MSDN il file " Schema.ini " ;.

Controlla

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

Devi dire al driver di scansionare tutte le righe per determinare lo schema. Altrimenti se le prime poche righe sono numeriche e le restanti sono alfanumeriche, le celle alfanumeriche saranno vuote.

Come Rory , ho scoperto che dovevo creare un file schema.ini in modo dinamico perché non c'è modo di programmarlo dire al conducente di scansionare tutte le righe. (questo non è il caso dei file Excel)

Devi avere MaxScanRows = 0 nel tuo schema.ini

Ecco un esempio di codice:

    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 =
            <*>quot;[{filename}]{Environment.NewLine}" +
            <*>quot;Format=CSVDelimited{Environment.NewLine}" +
            <*>quot;ColNameHeader={(isFirstRowHeader ? "True" : "False")}{Environment.NewLine}" +
            <*>quot;MaxScanRows=0{Environment.NewLine}" +
            <*>quot; ; scan all rows for data type{Environment.NewLine}" +
            <*>quot; ; 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 =
                <*>quot;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);
            }
        }
    }

Dovrai apportare alcune modifiche se lo stai eseguendo nella stessa directory in più thread contemporaneamente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top