Überprüfen Sie, ob ein Pfad in Python gültig ist, ohne eine Datei am Ziel des Pfads zu erstellen

90

Ich habe einen Pfad (einschließlich Verzeichnis und Dateiname).
Ich muss testen, ob der Dateiname gültig ist, z. B. ob das Dateisystem es mir ermöglicht, eine Datei mit einem solchen Namen zu erstellen.
Der Dateiname enthält einige Unicode-Zeichen .

Es ist sicher anzunehmen, dass das Verzeichnissegment des Pfads gültig und zugänglich ist ( ich habe versucht, die Frage allgemeiner anwendbar zu machen, und anscheinend bin ich zu weit gegangen ).

Ich möchte sehr gerne nichts entkommen müssen, wenn ich nicht muss .

Ich würde einige der Beispielzeichen posten, mit denen ich es zu tun habe, aber anscheinend werden sie automatisch vom Stapelaustauschsystem entfernt. Wie auch immer, ich möchte Standard-Unicode-Entitäten wie beibehalten öund nur Dinge maskieren, die in einem Dateinamen ungültig sind.


Hier ist der Haken. Möglicherweise befindet sich bereits eine Datei am Ziel des Pfads (oder auch nicht). Ich muss diese Datei behalten, wenn sie existiert, und keine Datei erstellen, wenn sie nicht existiert.

Grundsätzlich mag ich prüfen , ob ich könnte auf einen Pfad schreiben , ohne tatsächlich zu öffnen den Weg für das Schreiben (und die automatische Erstellung der Datei / Datei clobbering dass in der Regel bringt).

So wie:

try:
    open(filename, 'w')
except OSError:
    # handle error here

von hier

Ist nicht akzeptabel, da dadurch die vorhandene Datei überschrieben wird, die ich nicht berühren möchte (falls vorhanden), oder die Datei erstellt wird, wenn dies nicht der Fall ist.

Ich weiß, dass ich tun kann:

if not os.access(filePath, os.W_OK):
    try:
        open(filePath, 'w').close()
        os.unlink(filePath)
    except OSError:
        # handle error here

Aber das wird die Datei an der erstellenfilePath , die ich dann müsste os.unlink.

Am Ende scheint es, als würde man 6 oder 7 Zeilen ausgeben, um etwas zu tun, das so einfach os.isvalidpath(filePath)oder ähnlich sein sollte.


Abgesehen davon muss dies unter (mindestens) Windows und MacOS ausgeführt werden, daher möchte ich plattformspezifische Dinge vermeiden.

``

Falscher Name
quelle
Wenn Sie testen möchten, ob der Pfad vorhanden ist und Sie darauf schreiben können, erstellen und löschen Sie einfach eine andere Datei. Geben Sie ihm einen eindeutigen Namen (oder so eindeutig wie möglich), um Probleme mit mehreren Benutzern / Threads zu vermeiden. Andernfalls möchten Sie die Berechtigungen überprüfen, die Sie direkt in das betriebssystemspezifische Durcheinander bringen.
Tony Hopkinson
3
@Tony Hopkinson - Grundsätzlich möchte ich prüfen , ob ich könnte auf einen Pfad schreiben , ohne tatsächlich das Schreiben etwas .
Gefälschter Name
Wenn Sie nichts zum Schreiben in die Datei haben, warum müssen Sie dann wissen, ob Sie dazu in der Lage sind?
Karl Knechtel
@Karl Knechtel - Wenn ich darauf schreibe und dort bereits eine Datei vorhanden ist, wird die vorhandene Datei beschädigt.
Gefälschter Name
2
@FakeName - Sie werden hier immer eine subtile Rennbedingung haben. Zwischen der Überprüfung, ob die Datei nicht vorhanden ist, aber erstellt werden könnte, und der anschließenden Erstellung der Datei könnte ein anderer Prozess sie erstellen, und Sie werden die Datei trotzdem überladen. Natürlich hängt es von Ihrer Verwendung ab, ob dies ein realistisches Problem ist oder nicht ...
Detly

Antworten:

146

tl; dr

Rufen Sie die is_path_exists_or_creatable()unten definierte Funktion auf.

Streng Python 3. Genau so rollen wir.

Eine Geschichte von zwei Fragen

Die Frage "Wie teste ich die Gültigkeit von Pfadnamen und bei gültigen Pfadnamen die Existenz oder Schreibbarkeit dieser Pfade?" ist eindeutig zwei getrennte Fragen. Beide sind interessant und haben hier keine wirklich zufriedenstellende Antwort erhalten ... oder irgendwo , wo ich sie finden könnte.

vikki ‚s Antwort haue wahrscheinlich in der Nähe, hat aber die bemerkenswerten Nachteile:

  • Unnötiges Öffnen ( ... und nicht zuverlässiges Schließen ) von Dateihandles.
  • Unnötiges Schreiben ( ... und dann nicht zuverlässiges Schließen oder Löschen ) von 0-Byte-Dateien.
  • Ignorieren von betriebssystemspezifischen Fehlern, die zwischen nicht ignorierbaren ungültigen Pfadnamen und ignorierbaren Dateisystemproblemen unterscheiden. Es überrascht nicht, dass dies unter Windows von entscheidender Bedeutung ist. ( Siehe unten. )
  • Ignorieren von Race-Bedingungen, die sich aus externen Prozessen ergeben, die gleichzeitig übergeordnete Verzeichnisse des zu testenden Pfadnamens (neu) verschieben. ( Siehe unten. )
  • Ignorieren von Verbindungszeitlimits, die sich aus diesem Pfadnamen ergeben, der sich auf veralteten, langsamen oder anderweitig vorübergehend unzugänglichen Dateisystemen befindet. Dies könnte öffentlich zugängliche Dienste potenziellen DoS- gesteuerten Angriffen aussetzen . ( Siehe unten. )

Wir werden das alles reparieren.

Frage 0: Was ist wieder die Gültigkeit von Pfadnamen?

Bevor wir unsere zerbrechlichen Fleischanzüge in die von Python durchsetzten Moshpits des Schmerzes schleudern, sollten wir wahrscheinlich definieren, was wir unter "Gültigkeit von Pfadnamen" verstehen. Was genau definiert Gültigkeit?

Mit "Gültigkeit von Pfadnamen" ist die syntaktische Korrektheit eines Pfadnamens in Bezug auf das Root-Dateisystem des aktuellen Systems gemeint - unabhängig davon, ob dieser Pfad oder dessen übergeordnete Verzeichnisse physisch vorhanden sind. Ein Pfadname ist unter dieser Definition syntaktisch korrekt, wenn er alle syntaktischen Anforderungen des Root-Dateisystems erfüllt.

Mit "Root-Dateisystem" meinen wir:

  • Auf POSIX-kompatiblen Systemen wird das Dateisystem im Stammverzeichnis ( /) bereitgestellt .
  • Unter Windows ist das Dateisystem mit %HOMEDRIVE%dem Laufwerksbuchstaben mit Doppelpunkt versehen, der die aktuelle Windows-Installation enthält (normalerweise, aber nicht unbedingt C:).

Die Bedeutung von "syntaktischer Korrektheit" hängt wiederum von der Art des Root-Dateisystems ab. Für ext4(und die meisten, aber nicht alle POSIX-kompatiblen) Dateisysteme ist ein Pfadname genau dann syntaktisch korrekt, wenn dieser Pfadname:

  • Enthält keine Null-Bytes (dh \x00in Python). Dies ist eine wichtige Voraussetzung für alle POSIX-kompatiblen Dateisysteme.
  • Enthält keine Pfadkomponenten, die länger als 255 Byte sind (z. B. 'a'*256in Python). Eine Pfadkomponente ist eine längste Teilkette eines Pfadnamen NO /Charakter (zB bergtatt, ind, i, und fjeldkamrenein dem Pfadnamen /bergtatt/ind/i/fjeldkamrene).

Syntaktische Korrektheit. Root-Dateisystem. Das ist es.

Frage 1: Wie sollen wir jetzt die Gültigkeit von Pfadnamen tun?

Das Überprüfen von Pfadnamen in Python ist überraschenderweise nicht intuitiv. Ich bin hier fest mit Fake Name einverstanden : Das offizielle os.pathPaket sollte eine sofort einsatzbereite Lösung dafür bieten. Aus unbekannten (und wahrscheinlich nicht zwingenden) Gründen ist dies nicht der Fall. Glücklicherweise ist Ihre eigene Ad-hoc - Lösung Abrollen nicht , dass stechende ...

OK, das ist es tatsächlich. Es ist haarig; es ist fies; es gluckst wahrscheinlich, wenn es knurrt und kichert, wenn es glüht. Aber was wirst du tun? Nuthin '.

Wir werden bald in den radioaktiven Abgrund des Low-Level-Codes abtauchen. Aber zuerst sprechen wir über High-Level-Shop. Der Standard os.stat()und die os.lstat()Funktionen lösen die folgenden Ausnahmen aus, wenn ungültige Pfadnamen übergeben werden:

  • Für Pfadnamen, die sich in nicht vorhandenen Verzeichnissen befinden, Instanzen von FileNotFoundError.
  • Für Pfadnamen, die sich in vorhandenen Verzeichnissen befinden:
    • Unter Windows Instanzen, WindowsErrorderen winerrorAttribut 123(dh ERROR_INVALID_NAME) ist.
    • Unter allen anderen Betriebssystemen:
    • Für Pfadnamen, die Null-Bytes (dh '\x00') enthalten, Instanzen von TypeError.
    • Für Pfadnamen, die Pfadkomponenten enthalten, die länger als 255 Byte sind, OSErrorderen Instanzen errcodeFolgendes sind:
      • Unter SunOS und der * BSD-Betriebssystemfamilie errno.ERANGE. (Dies scheint ein Fehler auf Betriebssystemebene zu sein, der auch als "selektive Interpretation" des POSIX-Standards bezeichnet wird.)
      • Unter allen anderen Betriebssystemen errno.ENAMETOOLONG.

Entscheidend ist, dass nur Pfadnamen in vorhandenen Verzeichnissen validiert werden können. Die Funktionen os.stat()und os.lstat()lösen generische FileNotFoundErrorAusnahmen aus, wenn übergebene Pfadnamen in nicht vorhandenen Verzeichnissen übergeben werden, unabhängig davon, ob diese Pfadnamen ungültig sind oder nicht. Die Existenz eines Verzeichnisses hat Vorrang vor der Ungültigkeit des Pfadnamens.

Bedeutet dies, dass Pfadnamen, die sich in nicht vorhandenen Verzeichnissen befinden, nicht validierbar sind? Ja - es sei denn, wir ändern diese Pfadnamen so, dass sie sich in vorhandenen Verzeichnissen befinden. Ist das aber überhaupt sicher machbar? Sollte das Ändern eines Pfadnamens nicht verhindern, dass wir den ursprünglichen Pfadnamen überprüfen?

Um diese Frage zu beantworten, erinnern Sie sich von oben daran, dass syntaktisch korrekte Pfadnamen im ext4Dateisystem keine Pfadkomponenten (A) enthalten, die Null-Bytes oder (B) mehr als 255 Bytes enthalten. Daher ist ein ext4Pfadname nur dann gültig, wenn alle Pfadkomponenten in diesem Pfadnamen gültig sind. Dies gilt für die meisten interessierenden realen Dateisysteme .

Hilft uns diese pedantische Einsicht tatsächlich? Ja. Es reduziert das größere Problem der Validierung des vollständigen Pfadnamens auf einen Schlag auf das kleinere Problem der Validierung aller Pfadkomponenten in diesem Pfadnamen. Jeder beliebige Pfadname kann plattformübergreifend überprüft werden (unabhängig davon, ob sich dieser Pfadname in einem vorhandenen Verzeichnis befindet oder nicht), indem der folgende Algorithmus befolgt wird:

  1. Teilen Sie diesen Pfadnamen in Pfadkomponenten auf (z. B. den Pfadnamen /troldskog/faren/vildin die Liste ['', 'troldskog', 'faren', 'vild']).
  2. Für jede solche Komponente:
    1. Verbinden Sie den Pfadnamen eines Verzeichnisses, das garantiert mit dieser Komponente vorhanden ist, zu einem neuen temporären Pfadnamen (z /troldskog. B. ).
    2. Geben Sie diesen Pfadnamen an os.stat()oder weiter os.lstat(). Wenn dieser Pfadname und damit diese Komponente ungültig ist, löst dieser Aufruf garantiert eine Ausnahme aus, die den Typ der Ungültigkeit und nicht eine generische FileNotFoundErrorAusnahme aufdeckt . Warum? Weil sich dieser Pfadname in einem vorhandenen Verzeichnis befindet. (Zirkuläre Logik ist zirkulär.)

Gibt es ein garantiertes Verzeichnis? Ja, aber normalerweise nur eines: das oberste Verzeichnis des Root-Dateisystems (wie oben definiert).

Das Übergeben von Pfadnamen, die sich in einem anderen Verzeichnis befinden (und daher nicht garantiert existieren), an die Rennbedingungen os.stat()oder os.lstat()lädt zu Rennbedingungen ein, selbst wenn dieses Verzeichnis zuvor auf Existenz getestet wurde. Warum? Weil externe Prozesse nicht daran gehindert werden können, dieses Verzeichnis gleichzeitig zu entfernen, nachdem dieser Test durchgeführt wurde, aber bevor dieser Pfadname an os.stat()oder übergeben wird os.lstat(). Entfessle die Hunde des wahnsinnigen Wahnsinns!

Der obige Ansatz hat auch einen erheblichen Nebeneffekt: Sicherheit. (Ist das nicht das schön?) Im Einzelnen:

Front-Faced-Anwendungen, die beliebige Pfadnamen aus nicht vertrauenswürdigen Quellen validieren, indem sie diese Pfadnamen einfach an DoS-Angriffe (Denial of Service) und andere Black-Hat-Spielereien weitergeben os.stat()oder os.lstat()für diese anfällig sind. Böswillige Benutzer versuchen möglicherweise wiederholt, Pfadnamen zu überprüfen, die sich auf Dateisystemen befinden, von denen bekannt ist, dass sie veraltet oder auf andere Weise langsam sind (z. B. NFS-Samba-Freigaben). In diesem Fall kann es vorkommen, dass blind eingehende Pfadnamen entweder mit Verbindungszeitüberschreitungen fehlschlagen oder mehr Zeit und Ressourcen verbrauchen als Ihre schwache Fähigkeit, der Arbeitslosigkeit standzuhalten.

Der obige Ansatz vermeidet dies, indem nur die Pfadkomponenten eines Pfadnamens anhand des Stammverzeichnisses des Stammdateisystems überprüft werden. (Wenn selbst das veraltet, langsam oder unzugänglich ist, haben Sie größere Probleme als die Überprüfung des Pfadnamens.)

Hat verloren? Toll. Lass uns anfangen. (Python 3 angenommen. Siehe "Was ist fragile Hoffnung für 300, Leycec ?")

import errno, os

# Sadly, Python fails to provide the following magic number for us.
ERROR_INVALID_NAME = 123
'''
Windows-specific error code indicating an invalid pathname.

See Also
----------
https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
    Official listing of all such codes.
'''

def is_pathname_valid(pathname: str) -> bool:
    '''
    `True` if the passed pathname is a valid pathname for the current OS;
    `False` otherwise.
    '''
    # If this pathname is either not a string or is but is empty, this pathname
    # is invalid.
    try:
        if not isinstance(pathname, str) or not pathname:
            return False

        # Strip this pathname's Windows-specific drive specifier (e.g., `C:\`)
        # if any. Since Windows prohibits path components from containing `:`
        # characters, failing to strip this `:`-suffixed prefix would
        # erroneously invalidate all valid absolute Windows pathnames.
        _, pathname = os.path.splitdrive(pathname)

        # Directory guaranteed to exist. If the current OS is Windows, this is
        # the drive to which Windows was installed (e.g., the "%HOMEDRIVE%"
        # environment variable); else, the typical root directory.
        root_dirname = os.environ.get('HOMEDRIVE', 'C:') \
            if sys.platform == 'win32' else os.path.sep
        assert os.path.isdir(root_dirname)   # ...Murphy and her ironclad Law

        # Append a path separator to this directory if needed.
        root_dirname = root_dirname.rstrip(os.path.sep) + os.path.sep

        # Test whether each path component split from this pathname is valid or
        # not, ignoring non-existent and non-readable path components.
        for pathname_part in pathname.split(os.path.sep):
            try:
                os.lstat(root_dirname + pathname_part)
            # If an OS-specific exception is raised, its error code
            # indicates whether this pathname is valid or not. Unless this
            # is the case, this exception implies an ignorable kernel or
            # filesystem complaint (e.g., path not found or inaccessible).
            #
            # Only the following exceptions indicate invalid pathnames:
            #
            # * Instances of the Windows-specific "WindowsError" class
            #   defining the "winerror" attribute whose value is
            #   "ERROR_INVALID_NAME". Under Windows, "winerror" is more
            #   fine-grained and hence useful than the generic "errno"
            #   attribute. When a too-long pathname is passed, for example,
            #   "errno" is "ENOENT" (i.e., no such file or directory) rather
            #   than "ENAMETOOLONG" (i.e., file name too long).
            # * Instances of the cross-platform "OSError" class defining the
            #   generic "errno" attribute whose value is either:
            #   * Under most POSIX-compatible OSes, "ENAMETOOLONG".
            #   * Under some edge-case OSes (e.g., SunOS, *BSD), "ERANGE".
            except OSError as exc:
                if hasattr(exc, 'winerror'):
                    if exc.winerror == ERROR_INVALID_NAME:
                        return False
                elif exc.errno in {errno.ENAMETOOLONG, errno.ERANGE}:
                    return False
    # If a "TypeError" exception was raised, it almost certainly has the
    # error message "embedded NUL character" indicating an invalid pathname.
    except TypeError as exc:
        return False
    # If no exception was raised, all path components and hence this
    # pathname itself are valid. (Praise be to the curmudgeonly python.)
    else:
        return True
    # If any other exception was raised, this is an unrelated fatal issue
    # (e.g., a bug). Permit this exception to unwind the call stack.
    #
    # Did we mention this should be shipped with Python already?

Getan. Schielen Sie nicht auf diesen Code. ( Es beißt. )

Frage 2: Möglicherweise ungültige Existenz oder Erstellbarkeit von Pfadnamen, was?

Das Testen der Existenz oder Erstellbarkeit möglicherweise ungültiger Pfadnamen ist angesichts der obigen Lösung meist trivial. Der kleine Schlüssel hier ist, die zuvor definierte Funktion aufzurufen, bevor der übergebene Pfad getestet wird:

def is_path_creatable(pathname: str) -> bool:
    '''
    `True` if the current user has sufficient permissions to create the passed
    pathname; `False` otherwise.
    '''
    # Parent directory of the passed path. If empty, we substitute the current
    # working directory (CWD) instead.
    dirname = os.path.dirname(pathname) or os.getcwd()
    return os.access(dirname, os.W_OK)

def is_path_exists_or_creatable(pathname: str) -> bool:
    '''
    `True` if the passed pathname is a valid pathname for the current OS _and_
    either currently exists or is hypothetically creatable; `False` otherwise.

    This function is guaranteed to _never_ raise exceptions.
    '''
    try:
        # To prevent "os" module calls from raising undesirable exceptions on
        # invalid pathnames, is_pathname_valid() is explicitly called first.
        return is_pathname_valid(pathname) and (
            os.path.exists(pathname) or is_path_creatable(pathname))
    # Report failure on non-fatal filesystem complaints (e.g., connection
    # timeouts, permissions issues) implying this path to be inaccessible. All
    # other exceptions are unrelated fatal issues and should not be caught here.
    except OSError:
        return False

Fertig und fertig. Außer nicht ganz.

Frage 3: Möglicherweise ungültige Pfadnamen oder Beschreibbarkeit unter Windows

Es gibt eine Einschränkung. Natürlich gibt es das.

Wie die offizielle os.access()Dokumentation zulässt:

Hinweis: E / A-Vorgänge können fehlschlagen, auch wenn dies os.access()darauf hinweist, dass sie erfolgreich sind, insbesondere bei Vorgängen auf Netzwerkdateisystemen, deren Berechtigungssemantik möglicherweise über das übliche POSIX-Berechtigungsbitmodell hinausgeht.

Zu keiner Überraschung ist Windows hier der übliche Verdächtige. Dank der umfassenden Verwendung von Zugriffssteuerungslisten (Access Control Lists, ACL) in NTFS-Dateisystemen lässt sich das vereinfachte POSIX-Berechtigungsbitmodell nur schlecht auf die zugrunde liegende Windows-Realität abbilden. Obwohl dies (wohl) nicht Pythons Schuld ist, könnte es dennoch für Windows-kompatible Anwendungen von Belang sein.

Wenn Sie es sind, wird eine robustere Alternative gesucht. Wenn der übergebene Pfad nicht vorhanden ist, versuchen wir stattdessen, eine temporäre Datei zu erstellen, die garantiert sofort im übergeordneten Verzeichnis dieses Pfads gelöscht wird - ein portablerer (wenn auch teurer) Test der Erstellbarkeit:

import os, tempfile

def is_path_sibling_creatable(pathname: str) -> bool:
    '''
    `True` if the current user has sufficient permissions to create **siblings**
    (i.e., arbitrary files in the parent directory) of the passed pathname;
    `False` otherwise.
    '''
    # Parent directory of the passed path. If empty, we substitute the current
    # working directory (CWD) instead.
    dirname = os.path.dirname(pathname) or os.getcwd()

    try:
        # For safety, explicitly close and hence delete this temporary file
        # immediately after creating it in the passed path's parent directory.
        with tempfile.TemporaryFile(dir=dirname): pass
        return True
    # While the exact type of exception raised by the above function depends on
    # the current version of the Python interpreter, all such types subclass the
    # following exception superclass.
    except EnvironmentError:
        return False

def is_path_exists_or_creatable_portable(pathname: str) -> bool:
    '''
    `True` if the passed pathname is a valid pathname on the current OS _and_
    either currently exists or is hypothetically creatable in a cross-platform
    manner optimized for POSIX-unfriendly filesystems; `False` otherwise.

    This function is guaranteed to _never_ raise exceptions.
    '''
    try:
        # To prevent "os" module calls from raising undesirable exceptions on
        # invalid pathnames, is_pathname_valid() is explicitly called first.
        return is_pathname_valid(pathname) and (
            os.path.exists(pathname) or is_path_sibling_creatable(pathname))
    # Report failure on non-fatal filesystem complaints (e.g., connection
    # timeouts, permissions issues) implying this path to be inaccessible. All
    # other exceptions are unrelated fatal issues and should not be caught here.
    except OSError:
        return False

Beachten Sie jedoch, dass auch dies möglicherweise nicht ausreicht.

Dank User Access Control (UAC), die ständig inimicable Windows Vista und alle nachfolgenden Iterationen davon liegt offensichtlich über Berechtigungen für Systemverzeichnisse betreffen. Wenn Nicht-Administrator - Benutzer in Dateien entweder den kanonischen zu erstellen versuchen C:\Windowsoder C:\Windows\system32Verzeichnissen, erlaubt UAC oberflächlich den Benutzer so zu tun , während tatsächlich alle erstellten Dateien in ein „Virtual Store“ Isolierung in dem Profil des Benutzers. (Wer hätte sich vorstellen können, dass die Täuschung von Benutzern langfristig schädliche Folgen haben würde?)

Das ist verrückt. Das ist Windows.

Beweise es

Wagen wir es? Es ist Zeit, die oben genannten Tests zu testen.

Da NULL das einzige Zeichen ist, das in Pfadnamen auf UNIX-orientierten Dateisystemen verboten ist, nutzen wir dies, um die kalte, harte Wahrheit zu demonstrieren - und ignorieren Sie nicht ignorierbare Windows-Spielereien, die mich offen gesagt gleichermaßen langweilen und verärgern:

>>> print('"foo.bar" valid? ' + str(is_pathname_valid('foo.bar')))
"foo.bar" valid? True
>>> print('Null byte valid? ' + str(is_pathname_valid('\x00')))
Null byte valid? False
>>> print('Long path valid? ' + str(is_pathname_valid('a' * 256)))
Long path valid? False
>>> print('"/dev" exists or creatable? ' + str(is_path_exists_or_creatable('/dev')))
"/dev" exists or creatable? True
>>> print('"/dev/foo.bar" exists or creatable? ' + str(is_path_exists_or_creatable('/dev/foo.bar')))
"/dev/foo.bar" exists or creatable? False
>>> print('Null byte exists or creatable? ' + str(is_path_exists_or_creatable('\x00')))
Null byte exists or creatable? False

Jenseits der Vernunft. Jenseits des Schmerzes. Sie werden Bedenken hinsichtlich der Python-Portabilität finden.

Cecil Curry
quelle
3
Ja, ich war es! Der Versuch, einen Cross-Portable-Regex zur Validierung von Pfadnamen zusammenzufassen, ist eine Übung der Sinnlosigkeit und wird in Fällen mit häufigem Rand garantiert fehlschlagen. Betrachten Sie die Pfadnamenlänge unter Windows beispielsweise: "Der maximale Pfad von 32.767 Zeichen ist ungefähr, da das Präfix '\\? \' Zur Laufzeit vom System zu einer längeren Zeichenfolge erweitert werden kann. Diese Erweiterung gilt für die Gesamtlänge . " Angesichts dessen ist es technisch unmöglich , einen regulären Ausdruck zu erstellen, der nur gültigen Pfadnamen entspricht. Es ist viel vernünftiger, stattdessen nur auf Python zu verzichten.
Cecil Curry
2
Ah. Ich sehe (widerwillig). Sie tun etwas noch Seltsameres, als eine Regex zu hacken. Ja, das wird garantiert noch härter scheitern. Damit wird auch die fragliche Frage, die nicht "Wie entferne ich ungültige Teilzeichenfolgen von einem Windows-spezifischen Basisnamen?", Nicht vollständig beantwortet. (... die Sie aus eigener Kraft nicht lösen können - wiederum aufgrund von Randfällen), aber "Wie teste ich die Gültigkeit von Pfadnamen und bei gültigen Pfadnamen die Existenz oder Schreibbarkeit dieser Pfade übertragbar?"
Cecil Curry
1
Dateisystemspezifische Einschränkungen sind definitiv ein berechtigtes Anliegen - aber es schneidet in beide Richtungen. Für nach vorne gerichtete Anwendungen, die beliebige Pfadnamen aus nicht vertrauenswürdigen Quellen verwenden, ist das blinde Ausführen von Lesevorgängen bestenfalls eine heikle Angelegenheit. In diesem Fall ist es nicht nur sinnvoll, die Verwendung des Root-Dateisystems zu erzwingen, sondern auch umsichtig. Für andere Anwendungen kann die Benutzerbasis jedoch vertrauenswürdig genug sein, um ungehinderten Dateisystemzugriff zu gewähren. Es ist ziemlich kontextabhängig, würde ich sagen. Danke, dass du das scharfsinnig bemerkt hast, niemand ! Ich werde oben eine Einschränkung hinzufügen.
Cecil Curry
2
Was die Nomenklatur angeht, bin ich ein pedantischer Fan davon, Testernamen voranzustellen is_. Das ist mein Charakterfehler. Dennoch, gebührend bemerkt: Sie können nicht jedem gefallen, und manchmal können Sie niemandem gefallen. ;)
Cecil Curry
1
Unter Fedora 24, Python 3.5.3, löst ein Pfadname mit eingebetteten Nullzeichen Folgendes aus: ValueError: Eingebettetes Nullbyte… muss Folgendes hinzufügen: `` `außer ValueError als exc: return False` `` vor oder nach dem TypeError-Trap.
mMerlin
46
if os.path.exists(filePath):
    #the file is there
elif os.access(os.path.dirname(filePath), os.W_OK):
    #the file does not exists but write privileges are given
else:
    #can not write there

Beachten Sie, dass path.existsdies aus mehr als nur einem Grund fehlschlagen kann, the file is not theresodass Sie möglicherweise feinere Tests durchführen müssen, z. B. zu testen, ob das enthaltende Verzeichnis vorhanden ist, und so weiter.


Nach meiner Diskussion mit dem OP stellte sich heraus, dass das Hauptproblem darin zu bestehen scheint, dass der Dateiname möglicherweise Zeichen enthält, die vom Dateisystem nicht zugelassen werden. Natürlich müssen sie entfernt werden, aber das OP möchte so viel menschliche Lesbarkeit aufrechterhalten, wie das Dateisystem zulässt.

Leider kenne ich keine gute Lösung dafür. In der Antwort von Cecil Curry wird das Problem jedoch genauer untersucht.

Niemand
quelle
Nein. Ich muss true zurückgeben, wenn die Datei im Pfad vorhanden ist oder erstellt werden kann . Ich muss false zurückgeben, wenn der Pfad ungültig ist (da in Windows ungültige Zeichen enthalten sind).
Gefälschter Name
or can be createdNun, das habe ich aus Ihrer Frage nicht gelesen. Das Lesen der Berechtigungen hängt in gewissem Maße von der Plattform ab.
Niemand
1
@Fake Name: Ja, es werden einige der Plattformabhängigkeiten entfernt, aber dennoch bieten einige Plattformen Dinge, die andere nicht tun, und es gibt keine einfache Möglichkeit, dies für alle zu verpacken. Ich habe meine Antwort aktualisiert und dort nachgesehen.
Niemand
1
Ich habe keine Ahnung, warum diese Antwort positiv bewertet wurde. Es kommt nicht annähernd an die Beantwortung der Kernfrage heran - kurz gesagt: "Bitte validieren Sie Pfadnamen?" Das Überprüfen von Pfadberechtigungen ist hier eine zusätzliche (und weitgehend ignorierbare) Frage. Während der Aufruf von os.path.exists(filePath)technisch Ausnahmen bei ungültigen Pfadnamen auslöst, müssten diese Ausnahmen explizit abgefangen und von anderen nicht verwandten Ausnahmen unterschieden werden. Darüber hinaus sind die gleichen Anruf kehrt Falsenicht auf die bestehenden Pfade , auf die der aktuelle Benutzer nicht haben Berechtigungen gelesen. Kurz gesagt, Schlechtigkeit.
Cecil Curry
1
@CecilCurry: So beantworten Sie Ihre Fragen: Sehen Sie sich den Bearbeitungsverlauf der Frage an. Wie bei den meisten Fragen war es am Anfang nicht so eindeutig, und selbst jetzt könnte der Wortlaut des Titels allein anders verstanden werden, als Sie gesagt haben.
Niemand
7

Wie wäre es mit Python 3:

try:
    with open(filename, 'x') as tempfile: # OSError if file exists or is invalid
        pass
except OSError:
    # handle error here

Mit der Option 'x' müssen wir uns auch keine Sorgen um die Rennbedingungen machen. Siehe Dokumentation hier .

Dies wird nun eine sehr kurzlebige temporäre Datei erstellen, wenn sie noch nicht existiert - es sei denn, der Name ist ungültig. Wenn Sie damit leben können, vereinfacht es die Dinge sehr.

Stephen Miller
quelle
2
Zu diesem Zeitpunkt ist das Projekt, das dies benötigte, so weit über den Punkt hinausgegangen, an dem eine Antwort sogar relevant ist, dass ich eine Antwort nicht wirklich akzeptieren kann.
Gefälschter Name
Ironischerweise ist die praktische Antwort nicht gut genug. Unabhängig davon, ich nehme an, Sie könnten sehen, ob die Datei existiert. Wenn dies der Fall ist, versuchen Sie, die Datei an eine andere Stelle zu kopieren, und versuchen Sie dann, sie zu überschreiben.
Der Matt
5
open(filename,'r')   #2nd argument is r and not w

öffnet die Datei oder gibt einen Fehler aus, wenn sie nicht vorhanden ist. Wenn ein Fehler vorliegt, können Sie versuchen, in den Pfad zu schreiben. Wenn dies nicht möglich ist, wird ein zweiter Fehler angezeigt

try:
    open(filename,'r')
    return True
except IOError:
    try:
        open(filename, 'w')
        return True
    except IOError:
        return False

Schauen Sie sich hier auch die Berechtigungen für Windows an

vikki
quelle
1
Um zu vermeiden, dass die Verknüpfung () der Testdatei explizit aufgehoben werden muss, können Sie diese verwenden tempfile.TemporaryFile(), um die temporäre Datei automatisch zu zerstören, wenn sie den Gültigkeitsbereich verlässt.
D_Bye
@FakeName Der Code ist anders, ich hätte im zweiten Teil os.access verwenden können, aber wenn Sie dem von mir angegebenen Link gefolgt wären, hätten Sie gesehen, dass dies keine gute Idee ist. Dadurch haben Sie die Möglichkeit, zu versuchen, das tatsächlich zu öffnen Weg zum Schreiben.
Vikki
Ich os.path.joinbaue meine Wege mit , damit ich keine Probleme habe. Außerdem bin ich nicht wirklich mit Verzeichnis Erlaubnis Fragen. Ich habe Verzeichnis (und Dateiname) Name Probleme.
Gefälschter Name
@FakeName In diesem Fall müssen Sie nur versuchen, es zu öffnen (Sie müssen nicht schreiben). Python gibt einen Fehler aus, wenn das filenameungültige Zeichen enthält. Ich habe die Antwort bearbeitet
Vikki
1
@HelgaIliashenko Beim Öffnen zum Schreiben wird eine vorhandene Datei überschrieben (leer gemacht), auch wenn Sie sie sofort schließen, ohne darauf zu schreiben. Deshalb habe ich zuerst zum Lesen geöffnet, weil auf diese Weise, wenn Sie keinen Fehler erhalten, Sie wissen, dass eine Datei vorhanden ist.
Vikki
-6

Versuchen Sie os.path.existsdies, um nach dem Pfad zu suchen und zurückzukehren, Truefalls vorhanden und Falsewenn nicht.

Nilesh
quelle
1
Nein. Ich muss true zurückgeben, wenn die Datei im Pfad vorhanden ist oder erstellt werden kann . Ich muss false zurückgeben, wenn der Pfad ungültig ist (da in Windows ungültige Zeichen enthalten sind).
Gefälschter Name
Welche Art von ungültigem Zeichen?
Nilesh
Keine Ahnung - das ist plattformspezifisch.
Gefälschter Name
2
Eigentlich dateisystemspezifisch.
Piotr Kalinowski