2016-06-17 5 views
9

Ich möchte nicht OS-Befehle verwenden, so dass es Betriebssystem abhängig ist.Wie überprüft man leere Gzip-Datei in Python

Dies ist in tarfile, tarfile.is_tarfile(filename) verfügbar, um zu überprüfen, ob eine Datei eine TAR-Datei ist oder nicht.

Ich finde keine relevanten Befehle im gzip Modul.


EDIT: Warum muss ich diese: Ich Liste der gzip-Dateien haben, diese variieren in Größen (1-10 GB) und einige sind leer. Bevor ich eine Datei lese (mit pandas.read_csv), möchte ich prüfen, ob die Datei leer ist oder nicht, denn für leere Dateien bekomme ich einen Fehler in pandas.read_csv. (Fehler wie: Erwartete 15 Spalten und gefunden -1)

Beispiel Befehle mit Fehlern:

import pandas as pd 
pd.read_csv('C:\Users\...\File.txt.gz', compression='gzip', names={'a', 'b', 'c'}, header=False) 
Too many columns specified: expected 3 and found -1 

Pandas Version 0.16.2

file zum Testen verwendet, es ist nur ein gzip leeren Datei.

+0

Es gibt ein Modul [gzip] (https://docs.python.org/2/library/gzip.html). Hast du das versucht? – SiHa

+0

Ich habe mir die Dokumentation angesehen, soweit ich weiß, dass es keine solche Methode gibt. Abgesehen von der Bezeichnung des Moduls möchten Sie etwas Spezifisches in diesem Modul vorschlagen? – Vipin

+0

Eine leere Datei hat die Größe 0. –

Antwort

1

versuchen, etwas wie folgt aus:

def is_empty(gzfile): 
    size = gzfile.read(). 
    if len(size) > 0: 
     gzfile.rewind() 
     return False 
    else: 
     return True 
+0

Ich habe Gzip-Datei, Ihre Antwort ist für Teer. – Vipin

+0

@Vipin sieh es dir an ... – wind85

+0

Leider schien das nicht zu funktionieren. 'gzip.read' gibt 'b'''für sogar ** eine leere Datei zurück ** was überhaupt kein gzip ist! –

5

Leider ist das gzip Modul aussetzen keine Funktionalität entspricht der -l Liste Option des gzip Programm. Aber in Python 3 können Sie leicht die Größe der unkomprimierten Daten erhalten, indem Sie die .seek Methode mit einem whence Argument von 2 aufrufen, was die Positionierung relativ zum Ende des (unkomprimierten) Datenstroms bedeutet.

.seek gibt die neue Byte-Position zurück, so gibt .seek(0, 2) den Byte-Offset des Endes der unkomprimierten Datei zurück, d. H. Die Dateigröße. Wenn also die nicht komprimierte Datei der .seek Anruf leer ist wird wieder 0.

import gzip 

def gz_size(fname): 
    with gzip.open(fname, 'rb') as f: 
     return f.seek(0, whence=2) 

Hier ist eine Funktion, die zwei auf Python arbeiten, 2.6.6 auf Python getestet.

def gz_size(fname): 
    f = gzip.open(fname, 'rb') 
    data = f.read() 
    f.close() 
    return len(data) 

Sie können über .seek und andere Methoden der GzipFile Klasse mit dem pydoc Programm lesen. Führen Sie einfach pydoc gzip in der Shell aus.


Alternativ, wenn Sie die Datei dekomprimieren vermeiden möchten Sie können (Art) lesen Sie die nicht komprimierte Datengröße direkt aus der .gz Datei. Die Größe wird in den letzten 4 Bytes der Datei als Little-Endian unsigned long gespeichert, also ist es tatsächlich die Größe modulo 2 ** 32, daher ist es nicht die wahre Größe, wenn die unkomprimierte Datengröße> = 4GB ist.

Dieser Code funktioniert sowohl in Python 2 als auch in Python 3.

import gzip 
import struct 

def gz_size(fname): 
    with open(fname, 'rb') as f: 
     f.seek(-4, 2) 
     data = f.read(4) 
    size = struct.unpack('<L', data)[0] 
    return size 

Allerdings ist diese Methode nicht zuverlässig, wie Mark Adler (gzip Co-Autor) in den Kommentaren erwähnt:

There are other reasons that the length at the end of the gzip file would not represent the length of the uncompressed data. (Concatenated gzip streams, padding at the end of the gzip file.) It should not be used for this purpose. It's only there as an integrity check on the data.


Hier ist eine andere Lösung. Es dekomprimiert nicht die gesamte Datei. Es gibt True zurück, wenn die unkomprimierten Daten in der Eingabedatei eine Länge von Null haben, aber es gibt auch True zurück, wenn die Eingabedatei selbst keine Länge hat. Wenn die Eingabedatei nicht die Länge null hat und keine gzip-Datei ist, wird OSError ausgelöst.

+0

Es gibt Fehler, Suche nicht unterstützt von Ende – Vipin

+0

@Vipin: Ah, ok. Es funktioniert auf Python 3. Ich werde eine Python 2-Version hinzufügen. –

+0

@Vipin Ärgerlich, die Python 2 'gzip .__ doc__' sagt, dass man einen 'woher'-arg mit' .seek' verwenden kann und dass der Standard 'woher' ist 0, aber es erwähnt _not_ nicht, dass ein' woher'-Argument von 2 wird nicht unterstützt. :( –

3

Wenn Sie überprüfen möchten, ob eine Datei eine gültige Gzip-Datei ist, können Sie sie öffnen und ein Byte daraus lesen. Wenn es erfolgreich ist, ist die Datei sehr wahrscheinlich eine gzip-Datei mit einem Vorbehalt: eine leere Datei ist auch erfolgreich diesem Test.

So erhalten wir

def is_gz_file(name): 
    with gzip.open(name, 'rb') as f: 
     try: 
      file_content = f.read(1) 
      return True 
     except: 
      return False 

Aber, wie ich bereits erwähnt, eine Datei, die leer ist (0 Bytes), gelingt es immer noch diesen Test, so dass Sie vielleicht, um sicherzustellen, würden wollen, dass die Datei nicht leer ist :

def is_gz_file(name): 
    if os.stat(name).ST_SIZE == 0: 
     return False 

    with gzip.open(name, 'rb') as f: 
     try: 
      file_content = f.read(1) 
      return True 
     except: 
      return False 

EDIT:

als die Frage ist nun auf "eine gzip-Datei, die leeren Inhalt nicht haben" geändert wurde, dann gilt:

def is_nonempty_gz_file(name): 
    with gzip.open(name, 'rb') as f: 
     try: 
      file_content = f.read(1) 
      return len(file_content) > 0 
     except: 
      return False 
1

Dies sollte es tun, ohne die Datei zu lesen.

1

der source code für die Version Python 2.7 des gzip Modul Blick durch, so scheint es sofort EOF zurückzukehren, nicht nur in dem Fall, dass die gzip-Datei Null-Bytes, sondern auch in dem Fall, dass die gzip-Datei Null Byte , was wohl ein Fehler ist.

Aber für Ihren speziellen Anwendungsfall können wir ein wenig besser sein, indem wir auch bestätigen, dass die gezippte Datei eine gültige CSV-Datei ist.

Dieser Code ...

import csv 
import gzip 

# Returns true if the specified filename is a valid gzip'd CSV file 
# If the optional 'columns' parameter is specified, also check that 
# the first row has that many columns 
def is_valid(filename, columns=None): 

    try: 

     # Chain a CSV reader onto a gzip reader 
     csv_file = csv.reader(gzip.open(filename)) 

     # This will try to read the first line 
     # If it's not a valid gzip, this will raise IOError 
     for row in csv_file: 

      # We got at least one row 
      # Bail out here if we don't care how many columns we have 
      if columns is None: 
       return True 

      # Check it has the right number of columns 
      return len(row) == columns 

     else: 

      # There were no rows 
      return False 

    except IOError: 

     # This is not a valid gzip file 
     return False 


# Example to check whether File.txt.gz is valid 
result = is_valid('File.txt.gz') 

# Example to check whether File.txt.gz is valid, and has three columns 
result = is_valid('File.txt.gz', columns=3) 

... sollte folgende Fehlerfälle richtig handhaben ...

  1. Die gzip-Datei ist null Byte
  2. Die gzip-Datei keine gültige gzip-Datei ist
  3. Die gzipped Datei ist null Byte
  4. Die gzipped Datei nicht Null-Bytes ist, enthält jedoch keine Daten CSV
  5. (Optional) die gzip-Datei enthält CSV-Daten, aber mit der falschen Anzahl der Spalten
3

UPDATE:

Ich würde dringend empfehlen, auf Pandas 0.18.1 (derzeit die neueste Version) zu aktualisieren, da jede neue Version von Pandas nette neue Funktionen einführt und Tonnen von alten Bugs behebt. Und die aktuelle Version (0.18.1) wird Ihre leeren Dateien sofort auspacken (siehe Demo unten).

Wenn Sie nicht auf eine neuere Version aktualisieren können, dann nutzen @MartijnPieters Empfehlung ab - die Ausnahme abfangen, anstelle der Prüfung (folgen Sie dem Easier to ask for forgiveness than permission Paradigma)

OLD Antwort: eine kleine Demonstration (mit Pandas 0.18.1), die leere Dateien, unterschiedliche Anzahl von Spalten usw. toleriert.

Ich habe versucht, Ihren Fehler zu reproduzieren (versucht leere CSV.gz, unterschiedliche Anzahl von Spalten, etc.), aber ich habe es nicht geschafft reproduzieren Sie Ihre Ausnahme mit Pandas v. 0.18.1:

import os 
import glob 
import gzip 
import pandas as pd 

fmask = 'd:/temp/.data/37874936/*.csv.gz' 

files = glob.glob(fmask) 

cols = ['a','b','c'] 

for f in files: 
    # actually there is no need to use `compression='gzip'` - pandas will guess it itself 
    # i left it in order to be sure that we are using the same parameters ... 
    df = pd.read_csv(f, header=None, names=cols, compression='gzip', sep=',') 
    print('\nFILE: [{:^40}]'.format(f)) 
    print('{:-^60}'.format(' ORIGINAL contents ')) 
    print(gzip.open(f, 'rt').read()) 
    print('{:-^60}'.format(' parsed DF ')) 
    print(df) 

Ausgang:

FILE: [ d:/temp/.data/37874936\1.csv.gz  ] 
-------------------- ORIGINAL contents --------------------- 
11,12,13 
14,15,16 


------------------------ parsed DF ------------------------- 
    a b c 
0 11 12 13 
1 14 15 16 

FILE: [ d:/temp/.data/37874936\empty.csv.gz ] 
-------------------- ORIGINAL contents --------------------- 

------------------------ parsed DF ------------------------- 
Empty DataFrame 
Columns: [a, b, c] 
Index: [] 

FILE: [d:/temp/.data/37874936\zz_5_columns.csv.gz] 
-------------------- ORIGINAL contents --------------------- 
1,2,3,4,5 
11,22,33,44,55 

------------------------ parsed DF ------------------------- 
     a b c 
1 2 3 4 5 
11 22 33 44 55 

FILE: [d:/temp/.data/37874936\z_bad_CSV.csv.gz ] 
-------------------- ORIGINAL contents --------------------- 
1 
5,6,7 
1,2 
8,9,10,5,6 

------------------------ parsed DF ------------------------- 
    a b  c 
0 1 NaN NaN 
1 5 6.0 7.0 
2 1 2.0 NaN 
3 8 9.0 10.0 

FILE: [d:/temp/.data/37874936\z_single_column.csv.gz] 
-------------------- ORIGINAL contents --------------------- 
1 
2 
3 

------------------------ parsed DF ------------------------- 
    a b c 
0 1 NaN NaN 
1 2 NaN NaN 
2 3 NaN NaN 

Können Sie eine CSV-Beispiel veröffentlichen, um diesen Fehler zu verursachen oder es irgendwo hochladen und hier einen Link?

+0

aktualisierte Frage mit erforderlichen Details. – Vipin

+0

@Vipin, ich habe meine Antwort aktualisiert - bitte überprüfen – MaxU

1

Leider wird ein solcher Versuch wahrscheinlich ein gewisses Mehr an Aufwand haben, wäre es wahrscheinlich billiger, die Ausnahme zu fangen, wie die Benutzer oben kommentiert. Eine gzip Datei einige feste Größe Regionen definiert, wie folgt:

Feste Regionen

Erstens gibt es 2 Bytes für die gzip magische Zahl, 1 Byte für das Komprimierungsverfahren, 1 Byte für die Flags, dann 4 weitere Bytes für die MTIME (Dateierstellungszeit), 2 Bytes für zusätzliche Flags und zwei weitere Bytes für das Betriebssystem, was insgesamt 12 Bytes ergibt.

Dies wird wie folgt aussieht (aus dem obigen Link):

+---+---+---+---+---+---+---+---+---+---+ 
|ID1|ID2|CM |FLG|  MTIME  |XFL|OS | (more-->) 
+---+---+---+---+---+---+---+---+---+---+ 

Variable Regionen

Dies ist jedoch, wo die Dinge schwierig werden (und nicht ohne Verwendung eines gzip-Modul oder ein anderes zu überprüfen Deflator).

Wenn zusätzliche Felder gesetzt wurden, gibt es eine variable Region XLEN danach eingestellt Bytes, die wie folgt aussieht:

(if FLG.FEXTRA set) 
+---+---+=================================+ 
| XLEN |...XLEN bytes of "extra field"...| (more-->) 
+---+---+=================================+ 

Danach gibt es dann eine Region von N Bytes, mit einem Null-terminierte Zeichenfolge für die Dateinamen (das ist standardmäßig gespeichert):

(if FLG.FNAME set) 
+=========================================+ 
|...original file name, zero-terminated...| (more-->) 
+=========================================+ 

Wir haben dann Anmerkungen:

(if FLG.FCOMMENT set) 
+===================================+ 
|...file comment, zero-terminated...| (more-->) 
+===================================+ 

Und schließlich, ein CRC16 (eine zyklische Redundanzprüfung, um sicherzustellen, dass der Dateiheader dann funktioniert, bevor wir in die variablen, komprimierten Daten gelangen.

Lösung

So wird jede Art von fester Größe geprüft, ob der Dateiname abhängig sein, oder, wenn es über die Leitung geschrieben wurde (gzip -c "Compress this data" > myfile.gz), anderen Bereichen, und Kommentare, die alle, die für definiert werden können Null-Dateien. Also, wie umgehen wir das? Einfache, verwenden Sie die gzip-Modul:

import gzip 

def check_null(path): 
    ''' 
    Returns an empty string for a null file, which is falsey, 
    and returns a non-empty string otherwise (which is truthey) 
    ''' 

    with gzip.GzipFile(path, 'rb') as f: 
     return f.read(1) 

Dies wird überprüfen, ob alle Daten innerhalb der erstellten Datei vorhanden ist, während nur einen kleinen Teil der Daten gelesen werden. Es dauert jedoch eine Weile, es ist einfacher, um Vergebung zu bitten, als um Erlaubnis zu bitten.

import contextlib  # python3 only, use a try/except block for Py2 
import pandas as pd 

with contexlib.suppress(pd.parser.CParserError as error): 
    df = pd.read_csv(path, compression='gzip', names={'a', 'b', 'c'}, header=False) 
    # do something here 
Verwandte Themen