Yolun hedefinde bir dosya oluşturmadan bir yolun Python'da geçerli olup olmadığını kontrol edin


98

Bir yolum var (dizin ve dosya adı dahil).
Dosya adının geçerli olup olmadığını test etmem gerekiyor, örneğin dosya sistemi böyle bir adla bir dosya oluşturmama izin verip vermeyeceğini.
Dosya adının içinde bazı unicode karakterler vardır.

Yolun dizin bölümünün geçerli ve erişilebilir olduğunu varsaymak güvenlidir ( soruyu genel olarak daha uygulanabilir hale getirmeye çalışıyordum ve görünüşe göre çok uzaklaştım ).

Ben çok ben olmadıkça bir şey kaçmak zorunda kalmak istemiyoruz var etmek.

İlgilendiğim bazı örnek karakterleri gönderirdim, ancak görünüşe göre bunlar yığın değişim sistemi tarafından otomatik olarak kaldırılıyor. Her neyse, standart unicode varlıklarını korumak öve sadece dosya adında geçersiz olan şeylerden kaçmak istiyorum .


İşte yakalama. Yolun hedefinde zaten bir dosya olabilir (veya olmayabilir). Varsa bu dosyayı tutmam, yoksa bir dosya oluşturmam gerekiyor.

Temelde ben kontrol etmek istiyorum olabilir bir yola yazma aslında yazma yolunu açmadan (ve otomatik dosya oluşturma / dosya genellikle gerektirmesidir clobbering).

Gibi:

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

buradan

Kabul edilemez, çünkü dokunmak istemediğim var olan dosyanın üzerine yazacak (eğer varsa) ya da yoksa adı geçen dosyayı oluşturacak.

Yapabileceğimi biliyorum:

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

Ancak bu, daha sonra yapmam gereken dosyayı oluşturacak .filePathos.unlink

Sonunda, bu kadar basit os.isvalidpath(filePath)veya benzer olması gereken bir şeyi yapmak için 6 veya 7 satır harcıyor gibi görünüyor .


Bir kenara, buna (en azından) Windows ve MacOS üzerinde çalışmam gerekiyor, bu yüzden platforma özgü şeylerden kaçınmak istiyorum.

''


Yolun var olup olmadığını test etmek istiyorsanız ve ona yazabiliyorsanız, başka bir dosya oluşturup silin. Çoklu kullanıcı / çoklu iş parçacığı sorunlarını önlemek için ona benzersiz bir ad verin (veya olabildiğince benzersiz). Aksi takdirde, sizi doğrudan işletim sistemine özgü karışıklığa bırakacak izinleri kontrol etmeyi düşünüyorsunuz.
Tony Hopkinson

3
@Tony Hopkinson - Temelde ben kontrol etmek istiyorum olabilir bir yola yazma aslında bir şey yazmadan .
Sahte İsim

Dosyaya yazacak bir şeyin yoksa, yapıp yapamayacağını neden bilmen gerekiyor?
Karl Knechtel

@Karl Knechtel - Ona yazarsam ve orada zaten bir dosya varsa, var olan dosyaya zarar verir.
Sahte İsim

2
@FakeName - Burada her zaman ince bir yarış durumunuz olacak. Dosyanın var olmadığını, ancak oluşturulabileceğini kontrol etmek ve ardından dosyayı oluşturmak arasında, başka bir süreç onu oluşturabilir ve yine de dosyayı bozarsınız. Tabii ki, bunun gerçekçi bir sorun olup olmadığı sizin kullanımınıza bağlıdır ...
detly

Yanıtlar:


155

tl; dr

is_path_exists_or_creatable()Aşağıda tanımlanan işlevi çağırın .

Kesinlikle Python 3. İşte böyle yuvarlanıyoruz.

İki Sorunun Hikayesi

"Yol adı geçerliliğini ve geçerli yol adları için bu yolların varlığını veya yazılabilirliğini nasıl test ederim?" Sorusu açıkça iki ayrı sorudur. Her ikisi de ilginç ve her ikisi de burada gerçekten tatmin edici bir cevap almamışlar ... ya da bulabileceğim herhangi bir yerde .

vikki 'ın cevabı muhtemelen yakın hews ama olağanüstü dezavantajları vardır:

  • Dosya tanıtıcılarını gereksiz yere açmak ( ... ve sonra güvenilir bir şekilde kapatmamak ).
  • 0 baytlık dosyaları gereksiz yere yazmak ( ... ve sonra güvenilir bir şekilde kapatma veya silme ).
  • Göz ardı edilemez geçersiz yol adları ile göz ardı edilebilir dosya sistemi sorunları arasında ayrım yapan işletim sistemine özgü hataları göz ardı etmek. Şaşırtıcı olmayan bir şekilde, bu Windows altında kritiktir. ( Aşağıya bakın. )
  • Test edilecek yol adının üst dizinlerini eşzamanlı olarak (yeniden) taşıyan harici işlemlerden kaynaklanan yarış koşullarının yok sayılması. ( Aşağıya bakın. )
  • Bu yol adının eski, yavaş veya başka bir şekilde geçici olarak erişilemeyen dosya sistemlerinde yer almasından kaynaklanan bağlantı zaman aşımlarının yok sayılması. Bu , halka açık hizmetleri potansiyel DoS kaynaklı saldırılara maruz bırakabilir . ( Aşağıya bakın. )

Bunların hepsini düzelteceğiz.

Soru 0: Yol Adı Geçerliliği Yine Nedir?

Kırılgan et kıyafetlerimizi pitonlarla dolu acı çukurlarına fırlatmadan önce, muhtemelen "yol adı geçerliliği" ile ne demek istediğimizi tanımlamalıyız. Geçerliliği tam olarak ne tanımlar?

"Yol adı geçerliliği" ile , mevcut sistemin kök dosya sistemine göre bir yol adının sözdizimsel doğruluğunu kastediyoruz - bu yolun veya üst dizinlerinin fiziksel olarak var olup olmadığına bakılmaksızın. Bir yol adı, kök dosya sisteminin tüm sözdizimsel gereksinimlerine uyuyorsa, bu tanıma göre sözdizimsel olarak doğrudur.

"Kök dosya sistemi" ile şunu kastediyoruz:

  • POSIX uyumlu sistemlerde, dosya sistemi kök dizine ( /) bağlanmıştır .
  • Windows'ta dosya sistemi %HOMEDRIVE%, geçerli Windows kurulumunu içeren iki nokta üst üste eklenmiş sürücü harfine bağlanmıştır (tipik olarak ancak zorunlu değildirC: ).

"Sözdizimsel doğruluk" un anlamı da kök dosya sisteminin türüne bağlıdır. Tüm POSIX uyumlu dosya sistemleri için ext4(ve hepsi değil ) dosya sistemleri için, bir yol adı sözdizimsel olarak doğrudur ancak ve ancak bu yol adı:

  • Boş bayt içermez (yani \x00Python'da). Bu, tüm POSIX uyumlu dosya sistemleri için zor bir gerekliliktir.
  • 255 bayttan uzun yol bileşeni içermez (örneğin, 'a'*256Python'da). Bir yol bileşeninin içeren bir yol adı boyunda bir en uzun alt dizgesi /karakteri (ör bergtatt, ind, ive fjeldkamreneyol adı /bergtatt/ind/i/fjeldkamrene).

Sözdizimsel doğruluk. Kök dosya sistemi. Bu kadar.

Soru 1: Yol Adı Geçerliliğini Şimdi Nasıl Yapalım?

Python'da yol adlarını doğrulamak şaşırtıcı bir şekilde sezgisel değildir. Burada Sahte İsim ile sıkı bir fikir birliği içindeyim : resmi os.pathpaket bunun için kullanıma hazır bir çözüm sunmalıdır. Bilinmeyen (ve muhtemelen zorlayıcı olmayan) nedenlerden dolayı, öyle değil. Neyse ki, kendi geçici çözümünüzü ortaya çıkarmak o kadar da yürek burkan değil ...

Tamam, aslında öyle. Tüylü; o iğrenç; Muhtemelen parlarken homurdanırken kıkırdar ve kıkırdar. Ama ne yapacaksın? Nuthin '.

Yakında düşük seviyeli kodun radyoaktif uçurumuna ineceğiz. Ama önce üst düzey mağazadan konuşalım. Standart os.stat()ve os.lstat()işlevler, geçersiz yol adları iletildiğinde aşağıdaki istisnaları ortaya çıkarır:

  • Varolmayan dizinlerde bulunan yol adları için FileNotFoundError.
  • Mevcut dizinlerde bulunan yol adları için:
    • Windows altında WindowsError, winerrorözniteliği 123(yani, ERROR_INVALID_NAME) olan örnekler .
    • Diğer tüm işletim sistemleri altında:
    • Boş bayt (yani, '\x00') içeren yol adları için TypeError.
    • 255 bayttan daha uzun yol bileşenleri içeren yol adları için OSError, errcodeözniteliği aşağıdaki gibi olan örnekler :
      • SunOS ve * BSD ailesi OSes altında errno.ERANGE,. (Bu, işletim sistemi düzeyinde bir hata gibi görünür, aksi takdirde POSIX standardının "seçici yorumu" olarak adlandırılır.)
      • Diğer tüm işletim sistemleri altında errno.ENAMETOOLONG.

En önemlisi, bu yalnızca mevcut dizinlerde bulunan yol adlarının doğrulanabilir olduğu anlamına gelir . os.stat()Ve os.lstat()işlevleri jenerik yükseltmek FileNotFoundErrorgeçti pathnames var olmayan dizinleri ikamet etmekte iken bu yol adlarını geçersiz olup olmadığı bakılmaksızın, istisnalar. Dizin varlığı, yol adı geçersizliğine göre önceliklidir.

Bu var olmayan dizinleri ikamet eden yol adlarını olduğunu anlamına mı geliyor değil valide? Evet - bu yol adlarını mevcut dizinlerde yer alacak şekilde değiştirmediğimiz sürece. Ancak bu güvenli bir şekilde mümkün müdür? Bir yol adını değiştirmek, orijinal yol adını doğrulamamızı engellememeli mi?

Bu soruyu yanıtlamak için, yukarıdan ext4dosya sistemindeki sözdizimsel olarak doğru yol adlarının boş bayt içeren yol bileşenleri (A) veya 255 bayttan uzun (B) içermediğini hatırlayın . Bu nedenle, bir ext4yol adı ancak ve ancak o yol adındaki tüm yol bileşenleri geçerliyse geçerlidir. Bu, gerçek dünyadaki çoğu dosya sistemi için geçerlidir .

Bu bilgiçlik taslayan içgörü bize gerçekten yardımcı oluyor mu? Evet. Tek seferde tam yol adının doğrulanması sorununu, yalnızca o yol adındaki tüm yol bileşenlerini doğrulamanın daha küçük sorununa indirgiyor. Herhangi bir rasgele yol adı, aşağıdaki algoritma izlenerek platformlar arası bir şekilde (bu yol adının mevcut bir dizinde bulunup bulunmadığına bakılmaksızın) doğrulanabilir:

  1. Bu yol adını yol bileşenlerine bölün (örneğin, /troldskog/faren/vildlisteye yol adı ['', 'troldskog', 'faren', 'vild']).
  2. Bu tür her bileşen için:
    1. Bu bileşenle var olması garantili bir dizinin yol adını yeni bir geçici yol adıyla birleştirin (örneğin, /troldskog).
    2. Bu yol adını os.stat()veya öğesine iletin os.lstat(). Bu yol adı ve dolayısıyla bileşen geçersizse, bu çağrının genel bir FileNotFoundErroristisna yerine geçersizlik türünü ortaya çıkaran bir istisna oluşturması garanti edilir . Neden? Çünkü bu yol adı var olan bir dizinde bulunuyor. (Dairesel mantık daireseldir.)

Var olması garanti edilen bir dizin var mı? Evet, ancak tipik olarak yalnızca bir: kök dosya sisteminin en üstteki dizini (yukarıda tanımlandığı gibi).

Başka bir dizinde bulunan (ve dolayısıyla varlığı garanti edilmez) yol adlarını, bu dizinin daha önce var olup olmadığı test edilmiş olsa bile yarış koşullarına davet eder os.stat()veya os.lstat()davet eder. Neden? Çünkü, harici işlemlerin bu test gerçekleştirildikten sonra ancak yol adı veya yoluna geçilmeden önce bu dizini aynı anda kaldırması engellenemez . Akıllara durgunluk veren deliliğin köpeklerini serbest bırakın!os.stat()os.lstat()

Yukarıdaki yaklaşımın da önemli bir yan faydası vardır: güvenlik. (Değil mi o güzel?) Özellikle:

Basitçe böyle pathnames geçirerek güvenilmeyen kaynaklardan gelen keyfi pathnames doğrulayarak uygulamaları öne bakan os.stat()veya os.lstat()Hizmet Reddi (DoS) saldırıları ve diğer siyah şapka maskaralık duyarlıdır. Kötü niyetli kullanıcılar, eski veya başka bir şekilde yavaş olduğu bilinen dosya sistemlerinde bulunan yol adlarını tekrar tekrar doğrulamaya çalışabilir (örneğin, NFS Samba paylaşımları); bu durumda, gelen yol adlarını körü körüne ifade etmek, bağlantı zaman aşımlarıyla sonuçta başarısız olmaya veya işsizliğe dayanmak için zayıf kapasitenizden daha fazla zaman ve kaynak tüketmeye meyillidir.

Yukarıdaki yaklaşım, yalnızca bir yol adının yol bileşenlerini kök dosya sisteminin kök dizinine göre doğrulayarak bunu ortadan kaldırır. (Hatta varsa en o bayat, yavaş ya da ulaşılmaz, sen yol adı doğrulama daha büyük sorunlarımız var.)

Kayıp? Harika. Hadi başlayalım. (Python 3 varsayılmıştır. Bkz. "300 için Kırılgan Umut Nedir, 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?

Bitti. Bu koda gözlerinizi kısmayın. ( Isırır. )

Soru 2: Muhtemelen Geçersiz Yol Adı Varlığı veya Oluşturulabilirlik, Eh?

Yukarıdaki çözüm göz önüne alındığında, muhtemelen geçersiz yol adlarının varlığını veya yaratılabilirliğini test etmek, çoğunlukla önemsizdir. Buradaki küçük anahtar , geçilen yolu test etmeden önce önceden tanımlanmış işlevi çağırmaktır :

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

Bitti ve bitti. Tam olarak değil.

Soru 3: Windows'ta Muhtemelen Geçersiz Yol Adı Varlığı veya Yazılabilirliği

Bir uyarı var. Tabii ki var.

Resmi os.access()belgelerin kabul ettiği gibi:

Not: G / Ç işlemleri os.access(), özellikle olağan POSIX izin biti modelinin ötesinde izin anlamlarına sahip olabilecek ağ dosya sistemlerindeki işlemler için, başarılı olacaklarını belirtse bile başarısız olabilir.

Şaşırtıcı olmayan bir şekilde, Windows burada olağan şüpheli. NTFS dosya sistemlerinde Erişim Kontrol Listelerinin (ACL) yaygın kullanımı sayesinde, basit POSIX izin biti modeli, temel Windows gerçekliğiyle zayıf bir şekilde eşleşir. Bu (tartışmalı bir şekilde) Python'un hatası olmasa da, yine de Windows uyumlu uygulamalar için endişe verici olabilir.

Eğer bu sensen, daha sağlam bir alternatif aranıyor. Aktarılan yol mevcut değilse , bunun yerine o yolun üst dizininde hemen silinmesi garantili geçici bir dosya oluşturmaya çalışırız - daha taşınabilir (pahalıysa) bir yaratıcılık testi:

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

Ancak, bu bile bu yeterli olmayabilir.

Kullanıcı Erişim Kontrolü (UAC) sayesinde, her zaman taklit edilemez Windows Vista ve bunun sonraki tüm yinelemeleri, açık bir şekilde sistem dizinleriyle ilgili izinler hakkında yalan söylüyor . Yönetici olmayan kullanıcılar, kurallı C:\Windowsveya C:\Windows\system32dizinlerde dosya oluşturmaya çalıştığında , UAC yüzeysel olarak kullanıcının bunu yapmasına izin verirken, oluşturulan tüm dosyaları aslında o kullanıcının profilinde bir "Sanal Mağaza" olarak izole eder. (Kullanıcıları aldatmanın uzun vadede zararlı sonuçları olacağını kim tahmin edebilirdi?)

Bu çılgınca. Bu Windows.

Kanıtla

Cesaret edebilir miyiz? Yukarıdaki testleri test etme zamanı.

NULL, UNIX odaklı dosya sistemlerindeki yol adlarında yasaklanan tek karakter olduğundan, bunu soğuk, zor gerçeği göstermek için kullanalım - açıkçası beni eşit ölçüde sıkan ve kızdıran göz ardı edilemeyen Windows saçmalıklarını görmezden gelin:

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

Akıl sağlığının ötesinde. Acının ötesinde. Python taşınabilirliği ile ilgili endişeleri bulacaksınız.


3
Evet, bendim! Çapraz taşınabilir yol adı doğrulayan bir normal ifadeyi bir araya getirmeye çalışmak, boşuna bir alıştırmadır ve yaygın uç durumlarda başarısız olacağı garantidir. Windows'ta yol adı uzunluğunu düşünün, örneğin: "32.767 karakterlik maksimum yol yaklaşıktır, çünkü '\\? \' Öneki sistem tarafından çalışma zamanında daha uzun bir dizeye genişletilebilir ve bu genişletme toplam uzunluk için geçerlidir . " Buna göre, sadece geçerli yol adlarıyla eşleşen bir normal ifade oluşturmak aslında teknik olarak mümkün değildir . Bunun yerine Python'a ertelemek çok daha mantıklı.
Cecil Curry

2
Ah. Ben (isteksizce) görüyorum. Bir normal ifadeyi hacklemekten daha da garip bir şey yapıyorsun . Evet, daha da fazla başarısız olacağı garantilidir. Bu da tamamen söz konusu soruyu ele başarısız değil "Ben Windows özgü basename geçersiz alt dizeleri şerit nasıl?" (... hangisini kendi ihmalinizle, yine uç durumlardan dolayı çözemezsiniz) ama "Yol adı geçerliliğini ve geçerli yol adları için bu yolların varlığını veya yazılabilirliğini çapraz portatif olarak nasıl test ederim?"
Cecil Curry

1
Dosya sistemine özgü kısıtlamalar kesinlikle geçerli bir sorundur - ancak her iki yolu da keser. Güvenilmeyen kaynaklardan rastgele yol adları kullanan öne bakan uygulamalar için, körü körüne okuma yapmak en iyi ihtimalle riskli bir önermedir; bu durumda, kök dosya sistemi kullanımını zorlamak sadece mantıklı değil, aynı zamanda tedbirlidir. Bununla birlikte, diğer uygulamalar için, kullanıcı tabanı, engelsiz dosya sistemi erişimi sağlayacak kadar güvenilir olabilir. Oldukça bağlama bağlı olduğunu söyleyebilirim. Bunu zekice not ettiğiniz için teşekkürler, Kimse ! Yukarıya bir uyarı ekleyeceğim.
Cecil Curry

2
İsimlendirme konusuna gelince, testçi isimlerinin ön ekini almanın bilgiçlikçi bir hayranıyım is_. Bu benim karakter kusurum. Bununla birlikte, usulüne uygun olarak not edildi: herkesi memnun edemezsiniz ve bazen kimseyi memnun edemezsiniz. ;)
Cecil Curry

1
Fedora 24, python 3.5.3'te, gömülü boş karakterlere sahip bir yol adı atar: ValueError: gömülü boş bayt… eklenmesi gerekir: `` ValueError as exc: false döndür '', TypeError tuzağından önce veya sonra.
mMerlin

47
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

Not path.existssadece daha nedenlerle başarısız olabilir the file is not thereEğer içeren dizin var ve bu yüzden eğer test gibi ince testler yapmak gerekebilir böylece.


OP ile yaptığım tartışmadan sonra, ana sorunun dosya adının dosya sistemi tarafından izin verilmeyen karakterler içerebileceği gibi göründüğü ortaya çıktı. Elbette kaldırılmaları gerekir, ancak OP dosya sisteminin izin verdiği kadar insani okuma kolaylığı sağlamak ister.

Maalesef bunun için iyi bir çözüm bilmiyorum. Ancak Cecil Curry'nin cevabı , sorunu tespit etmeye daha yakından bakıyor.


Hayır. Yoldaki dosya mevcutsa veya oluşturulabiliyorsa true döndürmem gerekiyor . Yol geçersizse (pencerelerde geçersiz karakterler içermesi nedeniyle) false döndürmem gerekiyor.
Sahte İsim

or can be createdPeki bunu sorunuzdan okumadım. İzinlerin okunması bir dereceye kadar platfrom bağımlı olacaktır.
Kimse Güneydoğu'dan

1
@Fake Name: Evet, platform bağımlılıklarının bir kısmını ortadan kaldıracak, ancak yine de bazı platformlar diğerlerinin sunmadığı şeyleri sunuyor ve bunların hepsi için bunu paketlemenin kolay bir yolu yok. Cevabımı güncelledim, oraya bir göz atın.
Güneydoğu'dan kimse

1
Bu cevaba neden olumlu oy verildiği hakkında hiçbir fikrim yok. Temel soruyu ele almakla uzaktan yakından ilgili değil - ki bu kısaca: "Yol adlarını doğrulayın, lütfen?" Yol izinlerinin doğrulanması burada yardımcı (ve büyük ölçüde göz ardı edilebilir) bir sorudur. Çağrının os.path.exists(filePath)teknik olarak geçersiz yol adlarında istisnalar ortaya çıkarmasına karşın , bu istisnaların açıkça yakalanması ve diğer ilgisiz istisnalardan ayırt edilmesi gerekir. Ayrıca, aynı çağrı False, geçerli kullanıcının okuma izinlerine sahip olmadığı mevcut yollara geri döner . Kısacası, kötülük.
Cecil Curry

1
@CecilCurry: Sorularınıza cevap vermek için: Sorunun düzenleme geçmişine bir göz atın. Çoğu soruda olduğu gibi, başlangıçta net bir kesim değildi ve şimdi bile başlığın ifadesi, söylediğinizden başka türlü anlaşılabilir.
Kimse SE

9

Python 3 ile şunlara ne dersiniz:

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

'X' seçeneğiyle yarış koşulları hakkında da endişelenmemize gerek yok. Buradaki belgelere bakın .

Şimdi, bu zaten mevcut değilse çok kısa ömürlü bir geçici dosya oluşturacaktır - ad geçersiz olmadığı sürece. Bununla yaşayabilirseniz, işleri çok kolaylaştırır.


2
Bu noktada, buna ihtiyaç duyan proje, bir cevabın konuyla alakalı olduğu ve bir cevabı gerçekten kabul edemediğim noktanın ötesine geçti.
Sahte İsim

İronik bir şekilde, pratik cevap yeterince iyi değil. Her şeye rağmen, dosyanın var olup olmadığını görebiliyordunuz. Varsa, dosyayı başka bir yere kopyalamayı deneyin ve ardından üzerine yazmaya çalışın.
The Matt

5
open(filename,'r')   #2nd argument is r and not w

dosyayı açar veya yoksa bir hata verir. Bir hata varsa, yola yazmaya çalışabilirsin, yapamazsan ikinci bir hata alırsın

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

Windows üzerindeki izinler hakkında da buraya bir göz atın


1
Test dosyasının bağlantısını açıkça kaldırma () ihtiyacını ortadan kaldırmak için tempfile.TemporaryFile(), geçici dosyayı kapsam dışına çıktığında otomatik olarak yok edecek olanı kullanabilirsiniz .
D_Bye

@FakeName Kod farklı, ikinci kısımda os.access kullanabilirdim ama verdiğim bağlantıyı takip ederseniz bunun iyi bir fikir olmadığını görmüşsünüzdür, bu size gerçekten açmaya çalışma seçeneği bırakıyor yazma yolu.
vikki

Yollarımı ile oluşturuyorum os.path.join, bu yüzden `` kaçan sorunlarım yok. Ayrıca, gerçekten dizin izin sorunları yaşamıyorum . Dizin (ve dosya adı) adı sorunları yaşıyorum .
Sahte İsim

@FakeName bu durumda sadece denemeniz ve açmanız gerekir (yazmanıza gerek yoktur), python filenamegeçersiz karakterler içeriyorsa hata verir . Cevabı düzenledim
vikki

1
@HelgaIliashenko Yazmak için açıldığında, yazmadan hemen kapatsanız bile mevcut bir dosyanın üzerine yazılır (boş bırakılır). Bu yüzden önce okumaya başlıyordum çünkü bu şekilde, eğer bir hata almazsanız, o zaman var olan bir dosya olduğunu bilirsiniz.
vikki

-7

try os.path.existsthis, yolu kontrol edecek ve Truevarsa ve Falsedeğilse geri dönecektir .


1
Hayır. Yoldaki dosya mevcutsa veya oluşturulabiliyorsa true döndürmem gerekiyor . Yol geçersizse (pencerelerde geçersiz karakterler içermesi nedeniyle) false döndürmem gerekiyor.
Sahte İsim

hangi tür geçersiz karakter?
Nilesh

Dunno - bu platforma özeldir.
Sahte İsim

2
Aslında dosya sistemine özel.
Piotr Kalinowski
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.