Automatisch erkennen wie CSV Dateien aufgebaut sind
CSV Dateien sind z.B. Exports aus Datenbanken oder Excel. Der Name steht für Comma-Separated Values und bedeutet, dass die Werte von einem Komma getrennt sind. Damit kann man einfach eine CSV importieren und zum Beispiel in PHP weiter verarbeiten.
Tja, einfach ist das natürlich nicht und jedes Programm erstellt andere CSV Dateien. Ich habe eine Funktion geschrieben die versucht automatisch zu erkennen wie eine CSV Datei aufgebaut ist. Hier ist eine Demo und im folgenden Artikel kommt die Funktionsweise.Die Funktion erwartet einen String, z.B.
"spalte1", "spalte2", "spalte3" und gibt dann ein Array zurück mit den Keys delimiter und enclosure. Aus dem obrigen Beispiel wäre das dann Array( [delimiter] => ; [enclosure] => ")
Hier kommt die komplette Funktion und anschließend ein paar erklärende Worte.
function csv_autodetect($string) {
$str = trim($string);
$char = '';
$e = '';
$d = array();
$open = false;
$strip = false;
for($i = 0; $i < strlen($str); $i++) {
[tab]$char = $str[$i];
[tab]if(preg_match('=[\W]=', $char) && ord($char) != 32) { [tab]
[tab] if($i == 0)
[tab] [tab]$e = $char;
[tab]
[tab] if($char == '\\')
[tab] [tab]$strip = true;
[tab] else
[tab] [tab]$strip = false;
[tab]
[tab] if($char == $e && !$strip) {
[tab] [tab]$open = $open ? false : true;
[tab] }
[tab] else {
[tab] [tab]if(!$open && !$strip)
[tab] [tab] $d[$char]++;
[tab] }
[tab]}
}
asort($d);
$dd = array_keys($d, array_pop($d));
$return['delimiter'] = $dd[0];
$return['enclosure'] = $e;
return $return;
}
Nachdem einige Variablen vorbelegt wurden, erfolgt die zeichenweise Untersuchung des Strings. Wenn es sich um ein Sonderzeichen handelt, dann beginnt die Untersuchung.
if($i == 0)
$e = $char;Wenn das erste Zeichen des Strings ein Sonderzeichen ist und kein Leerzeichen, dann ist es mit Sicherheit jenes, welches die Werte umfasst (e = Enclosure).if($char == '\\')
$strip = true;
else
$strip = false;Wenn es sich um ein Backslash handelt, dann wird das nächste Zeichen ignoriert. Damit sind dann Fälle wie "eine \"Spalte\""; auch möglich.if($char == $e && !$strip)
$open = $open ? false : true;Wenn es also das umschließende Zeichen ist, und nicht durch ein Backslash auskommentiert wurde ($strip), dann wird der Status von $open geändert. Durch diese Beachtung ist es dann möglich den Delimeter innerhalb einer Spaltenbezeichnung zu benutzen (z.B. "spalte1", "spalte1,5", "spalte2"). Wenn es sich nicht um das umschließende Zeichen handelt, dann muss weiter überlegt werden:if(!$open && !$strip)
$d[$char]++;Wenn sich das Zeichen nicht innerhalb einer Spaltenbezeichnung befindet und auch nicht auskommentiert wurde, dann wird das Arrayelement $d (d = Delimiter) mit dem Zeichen als Schlüssel um eins erhöht.
Der Grund dafür ist das eventuelle Auftreten von Sonderzeichen in einer Spaltenbezeichnung ohne Enclosure (z.B. spalte1, spalte2&3, spalte4). Am Ende wird das Zeichen als Delimeter erwartet welches am häufigsten gefunden wurde. Das gibt zwar ein Problem bei einem String wie c&a, p&c, m&m - aber dann muss man seine Felder halt mit einem Zeichen umschließen.
Ende! Wer jetzt in seinem Kopf die Idee spinnt daraus ein Tool zu bauen welches CSV Dateien in eine Datenbank importiert, dem sei geraten noch ein paar Tage zu warten oder mir eine Mail zu schicken. Hab da was tolles in der mache was demnächst veröffentlicht wird
.
Wer Ideen, Probleme und Verbesserungsvorschläge hat kann diese gerne in den Kommentaren los werden.
- Mehr zu Entwicklung, PHP
- Trackback
-
War der Artikel nützlich für dich? Dann:
Ich habe versucht ihr Encoding-Tool zu UTF8 zu verwenden
aber leider löst diese eine unhandled exception (IO) aus, wenn ich versuche eine Datei x.txt auf sich selbst zu konvertieren in einem batch process (folder y wird komplett auf utf8 konvertiert)
“cannot access the file”
klar, wird ja grad noch von gelesen
gibts da evtl ne möglichkeit, dass dies doch funktioniert?
danke, gruß Mark