2010-06-20 20 views
7

Ho un file di testo che assomiglia a questo:analisi di un file di testo CSV formattato

1,Smith, 249.24, 6/10/2010 
2,Johnson, 1332.23, 6/11/2010 
3,Woods, 2214.22, 6/11/2010 
1,Smith, 219.24, 6/11/2010 

ho bisogno di essere in grado di trovare l'equilibrio per un cliente su una determinata data.

Mi chiedo se dovrei:

A. Inizio dalla fine e leggere ogni riga in un array, uno alla volta. Controlla l'indice del cognome per vedere se è il cliente che stiamo cercando. Quindi, mostra l'indice di equilibrio della prima partita.

o

B. Usare RegEx per trovare una corrispondenza e visualizzarlo.

Non ho molta esperienza con RegEx, ma lo imparerò se non è un problema in una situazione come questa.

+3

Quanto è grande il file e lo è o derubato in alcun modo? –

+0

Sarebbe ordinato per data. La dimensione potrebbe essere di 3MB – tpow

+2

@cinqoTimo - E farai più ricerche su di esso? –

risposta

6

mi consiglia di utilizzare il progetto opensource FileHelpers: http://www.filehelpers.net/

Parte della torta:

Definire la classe:

[DelimitedRecord(",")] 
public class Customer 
{ 
    public int CustId; 

    public string Name; 

    public decimal Balance; 

    [FieldConverter(ConverterKind.Date, "dd-MM-yyyy")] 
    public DateTime AddedDate; 

} 

usarlo:

var engine = new FileHelperAsyncEngine<Customer>(); 

// Read 
using(engine.BeginReadFile("TestIn.txt")) 
{ 
    // The engine is IEnumerable 
    foreach(Customer cust in engine) 
    { 
     // your code here 
     Console.WriteLine(cust.Name); 

     // your condition >> add balance 
    } 
} 
1

Si noti che entrambe le opzioni eseguiranno la scansione del file. Questo va bene se vuoi solo cercare nel file per 1 elemento.

Se è necessario cercare più combinazioni client/data nello stesso file, è possibile prima analizzare il file in uno Dictionary<string, Dictionary <date, decimal>>.

Una risposta diretta: per una tantum, un RegEx sarà probabilmente più veloce.

1

Se lo stai leggendo, prenderei in considerazione la lettura dell'intero file in memoria usando StreamReader.ReadToEnd e poi trattandolo come una lunga stringa da cercare e quando trovi un record che vuoi guardare cerca solo il la riga precedente e quella successiva si interrompono e quindi si ha la riga della transazione desiderata.

Se è su un server o il file può essere aggiornato per tutto il tempo, questa potrebbe non essere una buona soluzione.

2

Penso che il modo più pulito sia quello di caricare l'intero file in una serie di oggetti personalizzati e lavorare con esso. Per 3 MB di dati, questo non sarà un problema. Se si desidera eseguire una ricerca completamente diversa in seguito, è possibile riutilizzare la maggior parte del codice. Lo farei in questo modo:

class Record 
{ 
    public int Id { get; protected set; } 
    public string Name { get; protected set; } 
    public decimal Balance { get; protected set; } 
    public DateTime Date { get; protected set; } 

    public Record (int id, string name, decimal balance, DateTime date) 
    { 
    Id = id; 
    Name = name; 
    Balance = balance; 
    Date = date; 
    } 
} 

… 

Record[] records = from line in File.ReadAllLines(filename) 
        let fields = line.Split(',') 
        select new Record(
        int.Parse(fields[0]), 
        fields[1], 
        decimal.Parse(fields[2]), 
        DateTime.Parse(fields[3]) 
        ).ToArray(); 

Record wantedRecord = records.Single 
         (r => r.Name = clientName && r.Date = givenDate); 
2

Questo sembra un layout di tipo CSV piuttosto semplice, che è abbastanza facile da elaborare. È possibile farlo effettivamente con ADO.Net e con il provider Jet, ma penso che probabilmente sia più facile a lungo termine elaborarlo da solo.

Quindi, per prima cosa, si desidera elaborare i dati di testo effettivi. Suppongo che sia ragionevole assumere ogni record è separato da qualche carattere di nuova linea, in modo da poter utilizzare il metodo ReadLine di raggiungere facilmente ogni record:

StreamReader reader = new StreamReader("C:\Path\To\file.txt") 
while(true) 
{ 
    var line = reader.ReadLine(); 
    if(string.IsNullOrEmpty(line)) 
     break; 
    // Process Line 
} 

E poi per elaborare ogni riga, è possibile dividere la stringa in virgola e memorizza i valori in una struttura dati.Quindi, se si utilizza una struttura di dati come questo:

public class MyData 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public decimal Balance { get; set; } 
    public DateTime Date { get; set; } 
} 

E si può elaborare i dati di linea con un metodo come questo:

public MyData GetRecord(string line) 
{ 
    var fields = line.Split(','); 
    return new MyData() 
    { 
     Id = int.Parse(fields[0]), 
     Name = fields[1], 
     Balance = decimal.Parse(fields[2]), 
     Date = DateTime.Parse(fields[3]) 
    }; 
} 

Ora, questo è l'esempio più semplice, e non lo fa conto per i casi in cui i campi potrebbero essere vuoti, nel qual caso sarà necessario supportare NULL per tali campi (utilizzando i tipi nullable int ?, decimal? e DateTime?) o definire un valore predefinito che verrebbe assegnato a tali valori.

Quindi, una volta che avete, è possibile memorizzare la raccolta di oggetti MyData in un elenco e facilmente eseguire calcoli basati su questo. Quindi, dato il vostro esempio di trovare l'equilibrio in una determinata data si potrebbe fare qualcosa di simile:

var data = customerDataList.First(d => d.Name == customerNameImLookingFor 
            && d.Date == dateImLookingFor); 

Dove customerDataList è la raccolta di MyData oggetti leggere dal file, customerNameImLookingFor è una variabile che contiene il nome del cliente, e customerDateImLookingFor è una variabile contenente la data.

Ho usato questa tecnica per elaborare i dati in file di testo in passato per file che vanno da un paio di record, a decine di migliaia di record, e funziona piuttosto bene.

+0

CSV presenta alcuni dettagli complicati (in particolare quando si gestiscono caratteri che sono anche metacaratteri), quindi è meglio non scrivere il parser. "Più facile"? Quasi. "È più facile fare male e poi venire in una produzione" è più probabile. –

+0

Tutto dipende da quale è il tuo input e da quale livello di controllo hai. Il formato più complicato in cui mi sia mai imbattuto nel mondo reale è un caso in cui nel campo c'era una virgola, nel qual caso ci sono doppi apici attorno al campo, che è di nuovo facile da gestire. Se ti trovi in ​​una situazione in cui stai ricevendo i dati in una varietà di formati, allora potrebbe essere meglio trovare un parser CSV che faccia ciò di cui hai bisogno. Ho trovato alcune situazioni in cui il provider Jet ADO.Net non si è rivelato più fragile e più incline agli errori rispetto a una semplice analisi. – ckramer

+0

Non sono d'accordo con questa risposta, questo non è l'approccio giusto. – Pierreten

1

Se è tutto ben formattato-CSV come questo allora mi piacerebbe usare qualcosa come la classe o la classe Microsoft.VisualBasic.TextFieldParser CSV veloce sopra il progetto di codice per leggere tutto in.

Il tipo di dati è un po 'complicato perché immagino che non tutti i clienti abbiano un record per tutti i giorni. Ciò significa che non puoi semplicemente avere un dizionario annidato per la tua ricerca. Invece, si desidera "indicizzare" prima il nome e poi la data, ma la forma del record della data è leggermente diversa. Penso di andare per qualcosa di simile mentre leggo in ogni disco:

Dictionary<string, SortedList<DateTime, double>> 
1

hey, hey, hey !!! perché non farlo con questo fantastico progetto su codeproject Linq to CSV, molto cool! solida roccia

Problemi correlati