Sprawdź, czy ścieżka jest prawidłowa w Pythonie bez tworzenia pliku w miejscu docelowym ścieżki

100

Mam ścieżkę (w tym katalog i nazwę pliku).
Muszę sprawdzić, czy nazwa-pliku jest prawidłowa, np. Czy system plików pozwoli mi stworzyć plik o takiej nazwie.
Nazwa pliku zawiera kilka znaków Unicode .

Można bezpiecznie założyć, że segment katalogu ścieżki jest prawidłowy i dostępny ( starałem się, aby pytanie było bardziej ogólne i najwyraźniej dotarłem za daleko ).

Bardzo nie chcę uciekać przed czymkolwiek, chyba że muszę .

Zamieszczałbym kilka przykładowych postaci, z którymi mam do czynienia, ale najwyraźniej są one automatycznie usuwane przez system wymiany stosów. W każdym razie chcę zachować standardowe jednostki Unicode, takie jak öi tylko uciec przed rzeczami, które są nieprawidłowe w nazwie pliku.


Oto haczyk. Plik docelowy ścieżki może (ale nie musi) już istnieć. Muszę zachować ten plik, jeśli istnieje, i nie tworzyć go, jeśli nie istnieje.

Zasadniczo chcę, aby sprawdzić, czy ja mógł pisać do ścieżki bez faktycznie otwierając drogę do pisania (i automatyczne tworzenie pliku / file przebijania, które zwykle pociąga za sobą).

Takie jak:

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

stąd

Jest nie do przyjęcia, ponieważ nadpisze istniejący plik, którego nie chcę dotykać (jeśli tam jest), lub utworzy wspomniany plik, jeśli go nie ma.

Wiem, że potrafię:

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

Ale to utworzy plik w katalogu filePath, który musiałbym wtedy zrobić os.unlink.

W końcu wygląda na to, że spędza 6 lub 7 linii, aby zrobić coś, co powinno być tak proste os.isvalidpath(filePath)lub podobne.


Na marginesie, potrzebuję tego do działania (przynajmniej) na Windows i MacOS, więc chciałbym uniknąć rzeczy specyficznych dla platformy.

``

Zmyślone imię
źródło
Jeśli chcesz sprawdzić, czy ścieżka istnieje i możesz do niej pisać, po prostu utwórz i usuń inny plik. Nadaj mu unikalną nazwę (lub tak unikalną, jak tylko możesz), aby uniknąć problemów z wieloma użytkownikami / wieloma wątkami. W przeciwnym razie patrzysz na sprawdzenie uprawnień, które przeniesie Cię bezpośrednio do specyficznego zamieszania systemu operacyjnego.
Tony Hopkinson
3
@Tony Hopkinson - Zasadniczo chcę sprawdzić gdybym mógł napisać do ścieżki bez faktycznie pisanie czegokolwiek .
Fałszywe nazwisko
Jeśli nie masz nic do zapisania w pliku, dlaczego musisz wiedzieć, czy jesteś w stanie?
Karl Knechtel
2
@FakeName - Tutaj zawsze będziesz mieć subtelne warunki wyścigu. Pomiędzy sprawdzeniem, czy plik nie istnieje, ale można go utworzyć, a następnie utworzeniem pliku, jakiś inny proces może go utworzyć, a mimo to plik zostanie przebity. Oczywiście zależy to od Twojego użytkowania, czy jest to realistyczny problem, czy nie ...
zdziwienie
1
Częściowo mógłbyś to sprawdzić os.path.isabs(PATH), ale to nie obejmuje ścieżki względnej :-(.
pevik

Odpowiedzi:

158

tl; dr

Zadzwoń do is_path_exists_or_creatable() funkcję zdefiniowaną poniżej.

Ściśle Python 3. Tak właśnie działamy.

Opowieść o dwóch pytaniach

Pytanie „Jak sprawdzić poprawność nazw ścieżek i, w przypadku prawidłowych ścieżek, istnienie lub możliwość zapisu tych ścieżek?” to wyraźnie dwa oddzielne pytania. Obie są interesujące i żadna z nich nie otrzymała tutaj naprawdę satysfakcjonującej odpowiedzi ... lub, cóż, gdziekolwiek , gdzie mogłem grep.

Vikki jest odpowiedź prawdopodobnie hews najbliżej, ale ma niezwykłe wady:

  • Niepotrzebnie się otwierają ( ... a potem nie zamykają się niezawodnie ) uchwytów pilników.
  • Niepotrzebne zapisywanie ( ... a następnie niepowodzenie w niezawodnym zamykaniu lub usuwaniu ) plików 0-bajtowych.
  • Ignorowanie błędów specyficznych dla systemu operacyjnego, rozróżniających między niepoprawnymi nazwami ścieżek, których nie można ignorować, a problemami z systemem plików, które można zignorować. Nic dziwnego, że jest to krytyczne w systemie Windows. ( Zobacz poniżej. )
  • Ignorowanie warunków wyścigu wynikających z zewnętrznych procesów jednocześnie (ponownego) przenoszenia katalogów nadrzędnych testowanej nazwy ścieżki. ( Zobacz poniżej. )
  • Ignorowanie limitów czasu połączenia wynikających z tej nazwy ścieżki rezydującej na przestarzałych, powolnych lub w inny sposób tymczasowo niedostępnych systemach plików. Może to narazić publiczne usługi na potencjalne ataki spowodowane atakiem DoS . ( Zobacz poniżej. )

Naprawimy to wszystko.

Pytanie # 0: Jaka jest ponownie ważność ścieżki?

Zanim wrzucimy nasze kruche kombinezony mięsne w podziurawione pytonami moshpity bólu, powinniśmy prawdopodobnie zdefiniować, co rozumiemy przez „ważność ścieżki”. Co dokładnie definiuje ważność?

Przez „ważność ścieżki” rozumiemy poprawność składniową ścieżki w odniesieniu do głównego systemu plików bieżącego systemu - niezależnie od tego, czy ta ścieżka lub jej katalogi nadrzędne istnieją fizycznie. W ramach tej definicji nazwa ścieżki jest poprawna składniowo, jeśli spełnia wszystkie wymagania składniowe głównego systemu plików.

Przez „główny system plików” rozumiemy:

  • W systemach zgodnych z POSIX system plików podłączony do katalogu głównego ( /).
  • W systemie Windows, system plików zamontowany %HOMEDRIVE%, litera dysku okrężnicy sufiksem zawierającym bieżącej instalacji systemu Windows (zazwyczaj, ale nie koniecznie C:).

Z kolei znaczenie „poprawności składniowej” zależy od typu głównego systemu plików. Dla ext4(i większości, ale nie wszystkich zgodnych z POSIX) systemów plików, nazwa ścieżki jest poprawna składniowo wtedy i tylko wtedy, gdy ta nazwa ścieżki:

  • Nie zawiera bajtów zerowych (tj. \x00W Pythonie). Jest to trudne wymaganie dla wszystkich systemów plików zgodnych z POSIX.
  • Nie zawiera składników ścieżki dłuższych niż 255 bajtów (np. 'a'*256W Pythonie). Składnik ścieżka jest najdłuższy podciąg z ścieżki nie zawierającej /znak (na przykład bergtatt, ind, ioraz fjeldkamrenew ścieżkę /bergtatt/ind/i/fjeldkamrene).

Poprawność składniowa. Główny system plików. Otóż ​​to.

Pytanie 1: Jak teraz powinniśmy sprawdzić ważność nazwy ścieżki?

Weryfikacja ścieżek w Pythonie jest zaskakująco nieintuicyjna. W tym przypadku zgadzam się z Fake Name : oficjalny os.pathpakiet powinien zapewniać gotowe rozwiązanie tego problemu. Z nieznanych (i prawdopodobnie niekwestionowanych) powodów tak nie jest. Na szczęście rozwijanie własnego rozwiązania ad hoc nie jest aż tak bolesne ...

OK, faktycznie jest. Jest włochaty; to jest paskudne; prawdopodobnie chichocze, burczy i chichocze, gdy się świeci. Ale co zrobisz? Nic.

Wkrótce zejdziemy w radioaktywną otchłań niskopoziomowego kodu. Ale najpierw porozmawiajmy o sklepie na wysokim poziomie. Standard os.stat()i os.lstat()funkcje zgłaszają następujące wyjątki po przekazaniu nieprawidłowych nazw ścieżek:

  • W przypadku nazw ścieżek znajdujących się w nieistniejących katalogach wystąpienia FileNotFoundError.
  • W przypadku ścieżek znajdujących się w istniejących katalogach:
    • W systemie Windows instancje, WindowsErrorktórych winerroratrybut to 123(tj ERROR_INVALID_NAME.).
    • We wszystkich innych systemach operacyjnych:
    • W przypadku ścieżek zawierających bajty o wartości null (tj. '\x00'), Wystąpienia TypeError.
    • W przypadku ścieżek zawierających składniki ścieżek dłuższe niż 255 bajtów, wystąpienia OSErrorktórych errcodeatrybutu to:
      • Pod SunOS i * BSD, rodzina systemów operacyjnych errno.ERANGE. (Wydaje się, że jest to błąd na poziomie systemu operacyjnego, inaczej określany jako „selektywna interpretacja” standardu POSIX).
      • We wszystkich innych systemach operacyjnych errno.ENAMETOOLONG.

Co najważniejsze, oznacza to, że tylko ścieżki znajdujące się w istniejących katalogach są weryfikowalne. Funkcje os.stat()i os.lstat()zgłaszają ogólne FileNotFoundErrorwyjątki, gdy przekazywane są nazwy ścieżek rezydujące w nieistniejących katalogach, niezależnie od tego, czy te nazwy ścieżek są nieprawidłowe, czy nie. Istnienie katalogu ma pierwszeństwo przed nieważnością nazwy ścieżki.

Czy to oznacza, że nazwy ścieżek znajdujące się w nieistniejących katalogach nie podlegają walidacji? Tak - chyba że zmodyfikujemy te ścieżki, aby znajdowały się w istniejących katalogach. Czy jest to jednak w ogóle możliwe do wykonania? Czy modyfikacja nazwy ścieżki nie powinna uniemożliwić nam sprawdzenia oryginalnej ścieżki?

Aby odpowiedzieć na to pytanie, przypomnij sobie z góry, że poprawne składniowo nazwy ścieżek w ext4systemie plików nie zawierają składników ścieżki (A) zawierających bajty zerowe lub (B) o długości przekraczającej 255 bajtów. Dlatego ext4nazwa ścieżki jest poprawna wtedy i tylko wtedy, gdy wszystkie składniki ścieżki w tej nazwie ścieżki są prawidłowe. Dotyczy to większości interesujących nas systemów plików w świecie rzeczywistym .

Czy ten pedantyczny wgląd rzeczywiście nam pomaga? Tak. Zmniejsza to większy problem związany z walidacją pełnej nazwy ścieżki za jednym zamachem do mniejszego problemu sprawdzania poprawności tylko wszystkich składników ścieżki w tej nazwie ścieżki. Dowolną dowolną nazwę ścieżki można zweryfikować (niezależnie od tego, czy ta ścieżka znajduje się w istniejącym katalogu, czy nie) w sposób wieloplatformowy, stosując następujący algorytm:

  1. Podziel tę nazwę ścieżki na składniki ścieżki (np. Ścieżkę /troldskog/faren/vilddo listy ['', 'troldskog', 'faren', 'vild']).
  2. Dla każdego takiego składnika:
    1. Połącz ścieżkę katalogu, który może istnieć z tym komponentem, do nowej tymczasowej nazwy ścieżki (np /troldskog.).
    2. Przekaż tę ścieżkę do os.stat()lub os.lstat(). Jeśli ta nazwa ścieżki, a tym samym ten składnik, jest nieprawidłowa, to wywołanie gwarantuje zgłoszenie wyjątku ujawniającego typ nieważności, a nie FileNotFoundErrorwyjątek ogólny . Czemu? Ponieważ ta nazwa ścieżki znajduje się w istniejącym katalogu. (Logika kołowa jest kołowa.)

Czy istnieje katalog na pewno istnieje? Tak, ale zazwyczaj tylko jeden: najwyższy katalog głównego systemu plików (zgodnie z powyższą definicją).

Przekazywanie nazw ścieżek znajdujących się w jakimkolwiek innym katalogu (a zatem nie gwarantuje się, że istnieją) do warunków wyścigu os.stat()lub powoduje os.lstat()zaproszenie do warunków wyścigu, nawet jeśli wcześniej sprawdzono, czy katalog ten istnieje. Czemu? Ponieważ nie można zapobiec równoczesnemu usuwaniu tego katalogu przez procesy zewnętrzne po wykonaniu tego testu, ale przed przekazaniem tej os.stat()nazwy ścieżki do lub os.lstat(). Uwolnij psy oszałamiającego szaleństwa!

Powyższe podejście ma również istotną zaletę: bezpieczeństwo. (Nie jest to miłe?) W szczególności:

Aplikacje frontowe weryfikujące dowolne ścieżki z niezaufanych źródeł, po prostu przekazując takie ścieżki do ataków typu Denial of Service (DoS) i innych oszustw typu „black hat” os.stat()lub os.lstat()są na nie podatne. Złośliwi użytkownicy mogą próbować wielokrotnie sprawdzać poprawność nazw ścieżek znajdujących się na systemach plików, o których wiadomo, że są przestarzałe lub w inny sposób powolne (np. Udziały NFS Samby); w takim przypadku ślepe rejestrowanie przychodzących nazw ścieżek może ostatecznie zakończyć się niepowodzeniem z powodu przekroczenia limitów czasu połączenia lub pochłonąć więcej czasu i zasobów niż Twoja słaba zdolność wytrzymania bezrobocia.

Powyższe podejście zapobiega temu, sprawdzając tylko składniki ścieżki ścieżki względem katalogu głównego głównego systemu plików. (Jeśli nawet to jest nieaktualne, powolne lub niedostępne, masz większe problemy niż weryfikacja ścieżki).

Stracony? Świetny. Zaczynajmy. (Założono Python 3. Zobacz „What Is Fragile Hope for 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?

Gotowe. Nie mruż oczy na ten kod. ( Gryzie. )

Pytanie 2: Możliwe, że istnieje nieprawidłowa nazwa ścieżki lub możliwość jej wykonania, co?

Testowanie istnienia lub możliwości tworzenia potencjalnie nieprawidłowych nazw ścieżek jest, biorąc pod uwagę powyższe rozwiązanie, w większości trywialne. Mały klucz polega na wywołaniu wcześniej zdefiniowanej funkcji przed przetestowaniem przekazanej ścieżki:

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

Gotowe i gotowe. Z wyjątkiem niezupełnie.

Pytanie # 3: Prawdopodobnie nieprawidłowe istnienie ścieżki lub możliwość zapisu w systemie Windows

Istnieje zastrzeżenie. Oczywiście, że tak.

Jak przyznaje oficjalna os.access()dokumentacja :

Uwaga: Operacje we / wy mogą się nie powieść nawet wtedy os.access(), gdy wskazują, że zakończyłyby się powodzeniem, szczególnie w przypadku operacji na sieciowych systemach plików, które mogą mieć semantykę uprawnień wykraczającą poza typowy model bitowy uprawnień POSIX.

Nic dziwnego, że podejrzanym jest tutaj zwykle Windows. Dzięki szerokiemu wykorzystaniu list kontroli dostępu (ACL) w systemach plików NTFS, uproszczony model bitowy uprawnień POSIX słabo odwzorowuje rzeczywistość systemu Windows. Chociaż nie jest to (prawdopodobnie) wina Pythona, może to jednak dotyczyć aplikacji zgodnych z systemem Windows.

Jeśli to ty, potrzebna jest solidniejsza alternatywa. Jeśli przekazana ścieżka nie istnieje, zamiast tego spróbujemy utworzyć plik tymczasowy z gwarancją natychmiastowego usunięcia w katalogu nadrzędnym tej ścieżki - bardziej przenośny (jeśli kosztowny) test kreatywności:

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

Należy jednak pamiętać, że nawet to może nie wystarczyć.

Dzięki kontroli dostępu użytkownika (UAC), zawsze niemożliwy do wyobrażenia system Windows Vista i wszystkie jego kolejne iteracje rażąco kłamie na temat uprawnień odnoszących się do katalogów systemowych. Kiedy użytkownicy niebędący administratorami próbują tworzyć pliki w katalogu kanonicznym C:\Windowslub C:\Windows\system32katalogach, UAC powierzchownie zezwala użytkownikowi na to, podczas gdy w rzeczywistości izoluje wszystkie utworzone pliki do „magazynu wirtualnego” w profilu tego użytkownika. (Kto mógł sobie wyobrazić, że oszukiwanie użytkowników będzie miało szkodliwe długoterminowe konsekwencje?)

To jest szalone. To jest Windows.

Udowodnij to

Czy mamy odwagę? Czas przetestować powyższe testy.

Ponieważ NULL jest jedyną postacią zabronioną w nazwach ścieżek w systemach plików zorientowanych na UNIX, wykorzystajmy to, aby zademonstrować zimną, twardą prawdę - ignorując nie dające się zignorować shenanigans Windows, które szczerze mnie nudzą i złości w równym stopniu:

>>> 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

Poza zdrowiem psychicznym. Poza bólem. Znajdziesz obawy dotyczące przenośności języka Python.

Cecil Curry
źródło
3
Tak, to byłem ja! Próba połączenia ze sobą krzyżowo przenośnego wyrażenia regularnego walidującego nazwę ścieżki jest daremnym ćwiczeniem i gwarantuje niepowodzenie w przypadku typowych krawędzi. Weź pod uwagę długość ścieżki w systemie Windows, na przykład: „Maksymalna ścieżka składająca się z 32767 znaków jest przybliżona, ponieważ przedrostek„ \\? \ ”Może być rozszerzany przez system do dłuższego ciągu w czasie wykonywania, a to rozszerzenie dotyczy całkowitej długości ”. Biorąc to pod uwagę, jest technicznie niewykonalne skonstruowanie wyrażenia regularnego pasującego tylko do prawidłowych nazw ścieżek. O wiele rozsądniej jest po prostu przejść do Pythona.
Cecil Curry
2
Ach. Rozumiem (niechętnie). Robisz coś jeszcze dziwniejszego niż hakowanie wyrażenia regularnego. Tak, że gwarantowana jest na niepowodzenie jeszcze trudniejsze. To również całkowicie nie odpowiada na pytanie, które nie brzmi: „Jak usunąć nieprawidłowe podciągi z nazwy bazowej specyficznej dla systemu Windows?” (... których, przez własne zaniedbanie, nie udaje Ci się rozwiązać - ponownie z powodu skrajnych przypadków), ale „Jak mogę przetestować krzyżowo poprawność nazw ścieżek i, w przypadku prawidłowych nazw ścieżek, istnienie lub możliwość zapisu tych ścieżek?”
Cecil Curry
1
Ograniczenia specyficzne dla systemu plików są z pewnością ważnym problemem - ale działają w obie strony. W przypadku aplikacji korzystających z dowolnych ścieżek z niezaufanych źródeł ślepe wykonywanie odczytów jest w najlepszym przypadku ryzykowną propozycją; w tym przypadku wymuszenie użycia głównego systemu plików jest nie tylko rozsądne, ale i rozważne. Jednak w przypadku innych aplikacji baza użytkowników może być na tyle godna zaufania, aby udzielać nieograniczonego dostępu do systemu plików. Powiedziałbym, że jest to dość zależne od kontekstu. Dzięki za sprytne zauważenie, Nikt ! Dodam powyżej zastrzeżenie.
Cecil Curry
2
Jeśli chodzi o nomenklaturę, jestem pedantycznym fanem poprzedzania nazw testerów przez is_. To jest wada mojego charakteru. Niemniej jednak, należycie zauważono: nie możesz zadowolić wszystkich, a czasami nie możesz nikogo zadowolić. ;)
Cecil Curry
1
W Fedorze 24, python 3.5.3, nazwa ścieżki z osadzonymi znakami null wyrzuca: ValueError: osadzony bajt null… należy dodać: `` z wyjątkiem ValueError as exc: return False '' przed lub po pułapce TypeError.
mMerlin
48
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

Zauważ, że path.existsmoże się to nie powieść z wielu powodów, niż tylko the file is not theredlatego, że być może będziesz musiał wykonać dokładniejsze testy, takie jak testowanie, jeśli katalog zawierający taki istnieje i tak dalej.


Po mojej dyskusji z OP okazało się, że głównym problemem wydaje się być to, że nazwa pliku może zawierać znaki niedozwolone przez system plików. Oczywiście należy je usunąć, ale OP chce zachować tyle czytelności dla ludzi, na ile pozwala system plików.

Niestety nie znam dobrego rozwiązania tego problemu. Jednak odpowiedź Cecila Curry'ego przygląda się bliżej wykryciu problemu.

Nikt nie odchodzi od SE
źródło
Nie. Muszę zwrócić wartość true, jeśli plik w ścieżce istnieje lub można go utworzyć . Muszę zwrócić false, jeśli ścieżka jest nieprawidłowa (ze względu na nieprawidłowe znaki w systemie Windows).
Fałszywe imię
or can be createdcóż, nie przeczytałem tego z twojego pytania. Odczytywanie uprawnień będzie do pewnego stopnia zależne od platformy.
Nikt nie odchodzi od SE
1
@ Fake Name: Tak, usunie to niektóre zależności platformy, ale nadal niektóre platformy oferują rzeczy, których inne nie oferują, i nie ma łatwego sposobu na zapakowanie tego dla wszystkich. Zaktualizowałem odpowiedź, zajrzyj tam.
Nikt nie wyprowadza się z SE
1
Nie mam pojęcia, dlaczego głosowano za tą odpowiedzią. Nie przylega to zdalnie do odpowiedzi na podstawowe pytanie - które, zwięźle, brzmi: „Proszę sprawdzić poprawność nazw ścieżek?” Weryfikacja uprawnień do ścieżek jest tutaj kwestią pomocniczą (iw dużej mierze ignorowaną). Chociaż wywołanie do os.path.exists(filePath)technicznie generuje wyjątki dla nieprawidłowych nazw ścieżek, te wyjątki musiałyby zostać jawnie przechwycone i odróżnione od innych niepowiązanych wyjątków. Ponadto to samo wywołanie powraca Falsena istniejących ścieżkach, do których bieżący użytkownik nie ma uprawnień do odczytu. Krótko mówiąc, zło.
Cecil Curry,
1
@CecilCurry: Aby odpowiedzieć na swoje pytania: spójrz na historię edycji pytania. Jak w przypadku większości pytań, na początku nie było to tak jasne, a nawet teraz samo sformułowanie tytułu może być zrozumiane inaczej niż powiedziałeś.
Nikt nie wyjeżdża z SE
9

W przypadku Pythona 3, co powiesz na:

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

Dzięki opcji „x” nie musimy też martwić się o warunki wyścigu. Zobacz dokumentację tutaj .

Teraz spowoduje to utworzenie bardzo krótkotrwałego pliku tymczasowego, jeśli jeszcze nie istnieje - chyba że nazwa jest nieprawidłowa. Jeśli możesz z tym żyć, to bardzo upraszcza sprawę.

Stephen Miller
źródło
2
W tym momencie projekt, który tego wymagał, wyszedł tak daleko poza punkt, w którym odpowiedź jest nawet istotna, że ​​tak naprawdę nie mogę jej zaakceptować.
Fałszywe imię
Jak na ironię, praktyczna odpowiedź nie wystarczy. Niezależnie od tego, przypuszczam, że mógłbyś sprawdzić, czy plik istnieje. Jeśli tak, spróbuj skopiować plik w inne miejsce, a następnie spróbuj go zastąpić.
Matt
5
open(filename,'r')   #2nd argument is r and not w

otworzy plik lub wyświetli błąd, jeśli nie istnieje. Jeśli wystąpi błąd, możesz spróbować napisać do ścieżki, jeśli nie możesz, pojawi się drugi błąd

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

Zajrzyj tutaj również na temat uprawnień w systemie Windows

Vikki
źródło
1
Aby uniknąć konieczności jawnego odłączania () pliku testowego, możesz użyć funkcji, tempfile.TemporaryFile()która automatycznie zniszczy plik tymczasowy, gdy wyjdzie poza zakres.
D_Bye
@FakeName Kod jest inny, mogłem użyć os.access w drugiej części, ale gdybyś skorzystał z linku, który podałem, zobaczyłbyś, że to nie jest dobry pomysł, to pozostawia ci możliwość próby otwarcia ścieżka do pisania.
Vikki
Buduję swoje ścieżki os.path.join, więc nie mam problemów z ucieczką. Co więcej, tak naprawdę nie mam problemów z uprawnieniami do katalogu . Mam problemy z nazwą katalogu (i nazwy pliku) .
Fałszywe imię
@FakeName w takim przypadku wystarczy spróbować go otworzyć (nie musisz pisać), python wyświetla błąd, jeśli filenamezawiera nieprawidłowe znaki.
Zmieniłem
1
@HelgaIliashenko Otwarcie do zapisu nadpisze istniejący plik (uczyni go pustym), nawet jeśli zamkniesz go natychmiast bez zapisywania do niego. Dlatego najpierw otwierałem się do czytania, ponieważ w ten sposób, jeśli nie pojawi się błąd, wiesz, że istnieje plik.
Vikki
-7

spróbuj os.path.existsto sprawdzi ścieżkę i zwróci, Truejeśli istnieje, a Falsejeśli nie.

Nilesh
źródło
1
Nie. Muszę zwrócić wartość true, jeśli plik w ścieżce istnieje lub można go utworzyć . Muszę zwrócić false, jeśli ścieżka jest nieprawidłowa (ze względu na nieprawidłowe znaki w systemie Windows).
Fałszywe imię
jaki typ nieprawidłowego znaku?
Nilesh
Nie wiem - to jest specyficzne dla platformy.
Fałszywe imię
2
Właściwie specyficzny dla systemu plików.
Piotr Kalinowski