Come dovrei rilevare quale delimitatore è usato in un file di testo?

Devo essere in grado di analizzare entrambi i file CSV e TSV. Non posso fare affidamento sugli utenti per conoscere la differenza, quindi vorrei evitare di chiedere all’utente di selezionare il tipo. C’è un modo semplice per rilevare quale delimitatore è in uso?

Un modo sarebbe quello di leggere in ogni riga e contare entrambe le tabs e le virgole e scoprire quale è il più utilizzato in ogni riga. Naturalmente, i dati potrebbero includere virgole o tabs, in modo che sia più facile a dirsi che a farsi.

Modifica: Un altro aspetto divertente di questo progetto è che dovrò anche rilevare lo schema del file quando lo leggerò perché potrebbe essere uno dei tanti. Ciò significa che non saprò quanti campi ho fino a quando non posso analizzarlo.

Puoi mostrare loro i risultati nella finestra di anteprima, in modo simile a come fa Excel. È abbastanza chiaro quando viene utilizzato il delimitatore sbagliato in quel caso. Potresti quindi consentire loro di selezionare un intervallo di delimitatori e avere l’aggiornamento dell’anteprima in tempo reale.

Quindi puoi semplicemente fare una semplice congettura sul delimitatore per iniziare (ad esempio, una virgola o una scheda vengono prima).

In Python, esiste una class Sniffer nel modulo csv che può essere usata per indovinare il delimitatore di un dato file e citare i caratteri. La sua strategia è (citata dalle docstring di csv.py):


[Primo, guarda] per il testo racchiuso tra due virgolette identiche (il probabile quotechar) che sono precedute e seguite dallo stesso carattere (il delimitatore probabile). Per esempio:

,'some text', 

La citazione con il maggior numero di vincite, uguale al delimitatore. Se non è presente alcuna quota, il delimitatore non può essere determinato in questo modo.

In tal caso, prova quanto segue:

Il delimitatore dovrebbe verificarsi lo stesso numero di volte su ogni riga. Tuttavia, a causa di dati malformati, potrebbe non farlo. Non vogliamo un approccio tutto o niente, quindi consentiamo piccole variazioni in questo numero.

  1. costruisci una tabella della frequenza di ogni personaggio su ogni linea.
  2. build una tabella di frequenze di questa frequenza (meta-frequenza?), ad esempio ‘x si è verificato 5 volte in 10 righe, 6 volte in 1000 righe, 7 volte in 2 righe’
  3. usa la modalità della meta-frequenza per determinare la frequenza prevista per quel personaggio
  4. scoprire con quale frequenza il personaggio raggiunge effettivamente questo objective
  5. il personaggio che meglio soddisfa il suo objective è il delimitatore

Per motivi di prestazioni, i dati vengono valutati in blocchi, in modo che possa provare e valutare la più piccola porzione ansible dei dati, valutando i blocchi aggiuntivi necessari.


Non ho intenzione di citare il codice sorgente qui – è nella directory Lib di ogni installazione Python.

Ricorda che CSV può anche usare il punto e virgola anziché le virgole come delimitatori (ad esempio nelle versioni tedesche di Excel, i CSV sono delimitati da punto e virgola perché le virgole vengono utilizzate come separatori decimali in Germania …)

Sai quanti campi dovrebbero essere presenti per linea? Se è così, vorrei leggere le prime righe del file e controllare in base a quello.

Nella mia esperienza, i dati “normali” contengono spesso virgole, ma raramente contengono caratteri di tabulazione. Ciò suggerirebbe di controllare un numero consistente di tabs nelle prime righe e scegliere tale scelta come ipotesi preferita. Certo, dipende esattamente da quali dati hai.

In definitiva, sarebbe del tutto ansible avere un file che è completamente valido per entrambi i formati, quindi non puoi renderlo assolutamente infallibile. Dovrà essere un lavoro “best effort”.

Mi sono imbattuto in un bisogno simile e ho pensato di condividere ciò che mi è venuto in mente. Non ho ancora eseguito molti dati attraverso di esso, quindi ci sono possibili casi limite. Inoltre, tieni presente che l’objective di questa funzione non è la certezza del 100% del delimitatore, ma la migliore ipotesi da presentare all’utente.

 ///  /// Analyze the given lines of text and try to determine the correct delimiter used. If multiple /// candidate delimiters are found, the highest frequency delimiter will be returned. ///  ///  /// string discoveredDelimiter = DetectDelimiter(dataLines, new char[] { '\t', '|', ',', ':', ';' }); ///  /// Lines to inspect /// Delimiters to search for /// The most probable delimiter by usage, or null if none found. public string DetectDelimiter(IEnumerable lines, IEnumerable delimiters) { Dictionary delimFrequency = new Dictionary(); // Setup our frequency tracker for given delimiters delimiters.ToList().ForEach(curDelim => delimFrequency.Add(curDelim, 0) ); // Get a total sum of all occurrences of each delimiter in the given lines delimFrequency.ToList().ForEach(curDelim => delimFrequency[curDelim.Key] = lines.Sum(line => line.Count(p => p == curDelim.Key)) ); // Find delimiters that have a frequency evenly divisible by the number of lines // (correct & consistent usage) and order them by largest frequency var possibleDelimiters = delimFrequency .Where(f => f.Value > 0 && f.Value % lines.Count() == 0) .OrderByDescending(f => f.Value) .ToList(); // If more than one possible delimiter found, return the most used one if (possibleDelimiters.Any()) { return possibleDelimiters.First().Key.ToString(); } else { return null; } } 

È in PHP, ma questo sembra abbastanza affidabile:

 $csv = 'something;something;something someotherthing;someotherthing;someotherthing '; $candidates = array(',', ';', "\t"); $csvlines = explode("\n", $csv); foreach ($candidates as $candidatekey => $candidate) { $lastcnt = 0; foreach ($csvlines as $csvline) { if (strlen($csvline) <= 2) continue; $thiscnt = substr_count($csvline, $candidate); if (($thiscnt == 0) || ($thiscnt != $lastcnt) && ($lastcnt != 0)) { unset($candidates[$candidatekey]); break; } $lastcnt = $thiscnt; } } $delim = array_shift($candidates); echo $delim; 

Quello che fa è il seguente: Per ogni delimitatore ansible specificato, legge ogni riga nel CSV e controlla se il numero di volte in cui ogni separatore si verifica è costante. In caso contrario, il seperatore candidato viene rimosso e alla fine si dovrebbe finire con un separatore.

Immagino che la soluzione suggerita sarebbe il modo migliore per andare. In un file CSV o TSV ben formato, il numero di virgole o tabulazioni rispettivamente per riga deve essere costante (nessuna variazione). Esegui il conteggio di ciascuno per ogni riga del file e controlla quale è costante per tutte le linee. Sembrerebbe abbastanza improbabile che il conteggio di entrambi i delimitatori per ogni riga sia identico, ma in questo caso inimmaginabilmente raro, si potrebbe ovviamente richiedere all’utente.

Se il numero di tabulazioni e virgole non è costante, visualizza un messaggio all’utente indicando che il file non è corretto, ma il programma pensa che sia un file (indipendentemente dal formato con la deviazione standard minima del delimitatore per riga).

Basta leggere poche righe, contare il numero di virgole e il numero di tabs e confrontarle. Se ci sono 20 virgole e nessuna tabulazione, è in CSV. Se ci sono 20 tabs e 2 virgole (forse nei dati), è in TSV.

Non esiste un modo “efficiente”.

Supponendo che ci sia un numero fisso di campi per riga e che qualsiasi virgola o tabulazione all’interno dei valori sia racchiusa tra virgolette (“), dovresti essere in grado di elaborarla sulla frequenza di ciascun carattere in ogni riga. t risolto, questo è più difficile, e se le virgolette non sono usate per racchiudere caratteri delimitanti altrimenti, sarà, a mio avviso, quasi imansible (e dipende dai dati, dalle specifiche locali).

Nella mia esperienza, i dati raramente contengono tabs, quindi una riga di campi delimitati da tabulazioni sarebbe (generalmente) abbastanza ovvia.

Le virgole sono più difficili, sebbene – specialmente se stai leggendo dati in locali non statunitensi. I dati numerici possono contenere un numero enorme di virgole se stai leggendo file generati fuori dal paese, poiché i numeri in virgola mobile spesso li contengono.

Alla fine, l’unica cosa sicura, però, è di solito da provare, quindi presentarlo all’utente e consentire loro di adattarsi, soprattutto se i tuoi dati conterranno virgole e / o tabs.

Suppongo che nel testo normale le tabulazioni siano molto rare tranne che come primi caratteri su una riga – pensate a paragrafi rientrati o codice sorgente. Penso che se trovi le tabs incorporate (ad esempio quelle che non seguono le virgole), puoi assumere che le tabs vengano utilizzate come delimitatori e che siano corrette per la maggior parte del tempo. Questo è solo un sospetto, non verificato con alcuna ricerca. Ovviamente darei all’utente la possibilità di sovrascrivere la modalità calcasting automaticamente.

Supponendo che tu abbia un set standard di colonne che ti aspetteresti …

Vorrei usare FileHelper (progetto open source su SourceForge). http://filehelpers.sourceforge.net/

Definisci due modelli di lettore, uno per i coma, uno per i tab.

Se il primo fallisce, prova il secondo.

Puoi controllare se una linea sta usando un delimitatore o un altro come questo:

 while ((line = readFile.ReadLine()) != null) { if (line.Split('\t').Length > line.Split(',').Length) // tab delimited or comma delimited? row = line.Split('\t'); else row = line.Split(','); parsedData.Add(row); }