Як перевірити, чи існує файл без винятків?


5604

Як перевірити, чи існує файл чи ні, не використовуючи tryоператор?


2
Це , здається, класичний ЕСПЦ проти LBYL обговорення см , наприклад , цей родинний питання: stackoverflow.com/questions/1835756/using-try-vs-if-in-python
Матв

Також це актуально, якщо ви хочете дізнатися, чи він присутній на диску (або безпосередньо, або як символічне посилання), чи можете ви з нього читати чи чи можете ви на нього написати.
Thorbjørn Ravn Andersen

Відповіді:


5153

Якщо ви перевіряєте причину, щоб ви могли зробити щось на кшталт if file_exists: open_it(), безпечніше використовувати tryнавколо спроби відкрити її. Перевірка, а потім відкриття ризикує видалити або перемістити файл або щось середнє, коли ви перевіряєте та намагаєтесь відкрити його.

Якщо ви не плануєте негайно відкривати файл, можете скористатися os.path.isfile

Повернення, Trueякщо шлях є існуючим звичайним файлом. З цього випливає символічне посилання, тому і islink (), і isfile () можуть бути істинними для одного шляху.

import os.path
os.path.isfile(fname) 

якщо вам потрібно бути впевненим, що це файл.

Починаючи з Python 3.4, pathlibмодуль пропонує об'єктно-орієнтований підхід (підтримується на pathlib2Python 2.7):

from pathlib import Path

my_file = Path("/path/to/file")
if my_file.is_file():
    # file exists

Щоб перевірити каталог, виконайте:

if my_file.is_dir():
    # directory exists

Щоб перевірити, чи Pathіснує об'єкт незалежно від файлу чи каталогу, використовуйте exists():

if my_file.exists():
    # path exists

Ви також можете використовувати resolve(strict=True)в tryблоці:

try:
    my_abs_path = my_file.resolve(strict=True)
except FileNotFoundError:
    # doesn't exist
else:
    # exists

40
що стосується першого зауваження (використовуйте "спробувати", якщо перевірте перед відкриттям), на жаль, це не спрацює, якщо ви хочете відкрити для додавання, переконавшись, що він існує раніше, оскільки режим "a" створить, якщо його немає.
makapuf

6
Зверніть увагу , що FileNotFoundErrorбуло введено в Python 3. Якщо ви також повинні підтримувати Python 2.7, а також Python 3, ви можете використовувати IOErrorзамість (які FileNotFoundErrorпідкласи) stackoverflow.com/a/21368457/1960959
scottclowe

7
@makapuf Ви можете відкрити його для "оновлення" ( open('file', 'r+')), а потім прагнути до кінця.
кирилл

2
@kyrill відкрити і шукати до кінця - це не те саме, що відкрити для додавання (принаймні, у системах posix). "режим додавання" завжди правильно запише в кінець файлу, навіть якщо інші речі змінюють файл між тим, коли він відкритий, і коли відбувається записування. Відкриття для запису / оновлення та прагнення до кінця ризикують зіпсувати дані файлу, якщо кілька речей намагаються записати в один і той же файл одночасно. На жаль, я вважаю, що єдиним безпечним для перегонів способом відкрити для додавання, переконавшись, що файл існує спочатку, є використання підпрограм os.open () нижнього рівня (O_APPEND без O_CREAT).
Foogod

1
@LorinczyZsigmond, що звучить як поведінка, що залежить від реалізації. Хочете поділитися джерелом?
kyrill

2111

Ви маєте os.path.existsфункцію:

import os.path
os.path.exists(file_path)

Це повертається Trueяк для файлів, так і для каталогів, але ви можете замість цього використовувати

os.path.isfile(file_path)

перевірити, чи це файл спеціально. З цього випливають символьні посилання.


965

На відміну від цього isfile(), exists()повернеться Trueза каталогами. Тож залежно від того, чи потрібно лише звичайні файли, а також каталоги, ви будете використовувати isfile()або exists(). Ось простий вихід REPL:

>>> os.path.isfile("/etc/password.txt")
True
>>> os.path.isfile("/etc")
False
>>> os.path.isfile("/does/not/exist")
False
>>> os.path.exists("/etc/password.txt")
True
>>> os.path.exists("/etc")
True
>>> os.path.exists("/does/not/exist")
False


320

Використовувати os.path.isfile()з os.access():

import os

PATH = './file.txt'
if os.path.isfile(PATH) and os.access(PATH, os.R_OK):
    print("File exists and is readable")
else:
    print("Either the file is missing or not readable")

60
наявність декількох умов, деякі з яких є зайвими, менш чітка і явна.
Вім

10
Він також є зайвим. Якщо файл не існує, os.access()повернеться помилковим.
користувач207421

9
@EJP У Linux файли можуть існувати, але не доступні.
e-info128

8
оскільки ви import os, вам не потрібно import os.pathзнову, оскільки це вже є частиною os. Вам потрібно просто імпортувати, os.pathякщо ви збираєтесь використовувати лише функції з, os.pathа не від osсебе, щоб імпортувати дрібніші речі, але як ви використовуєте, так os.accessі os.R_OKдругий імпорт не потрібен.
Єстер

286
import os
os.path.exists(path) # Returns whether the path (directory or file) exists or not
os.path.isfile(path) # Returns whether the file exists or not

2
Як правило, не надто добре називати змінні такими ж, як імена методів.
Homunculus Reticulli

245

Хоча майже всі можливі способи були перелічені у (принаймні одній із) існуючих відповідей (наприклад, додано конкретні матеріали Python 3.4 ), я спробую згрупувати все разом.

Примітка : кожен фрагмент стандартного коду бібліотеки Python, який я опублікую, належить до версії 3.5.3 .

Постановка проблеми :

  1. Перевірте наявність файлу ( аргументовано : також папка ("спеціальний" файл)?)
  2. Не використовуйте блоки try ( крім / else / нарешті)

Можливі рішення :

  1. [Python 3]: os.path. існує ( шлях ) (також перевірити інші члени сім'ї функції , такі як os.path.isfile, os.path.isdir, os.path.lexistsдля злегка різних поводжень)

    os.path.exists(path)

    Повернення, Trueякщо шлях посилається на існуючий шлях або відкритий дескриптор файлу. Повернення Falseдля розірваних символічних посилань. На деяких платформах ця функція може повернутися, Falseякщо не буде надано дозвіл на виконання os.stat () на запитуваному файлі, навіть якщо шлях фізично існує.

    Все добре, але якщо слідкувати за імпортом дерева:

    • os.path- posixpath.py ( ntpath.py )

      • genericpath.py , рядок ~ # 20 +

        def exists(path):
            """Test whether a path exists.  Returns False for broken symbolic links"""
            try:
                st = os.stat(path)
            except os.error:
                return False
            return True

    це просто спробувати / крім блоку навколо [Python 3]: os. stat ( шлях, *, dir_fd = Немає, follow_symlinks = True ) . Отже, ваш код спробуйте / за винятком безкоштовного, але нижче в framestack є (принаймні) один такий блок. Це стосується і інших функцій (у тому числі os.path.isfile ).

    1.1. [Пітон 3]: Шлях. is_file ()

    • Це любитель (і більш пітон IC) спосіб обробки шляхів, але
    • Під капотом він робить саме те саме ( pathlib.py , рядок ~ # 1330 ):

      def is_file(self):
          """
          Whether this path is a regular file (also True for symlinks pointing
          to regular files).
          """
          try:
              return S_ISREG(self.stat().st_mode)
          except OSError as e:
              if e.errno not in (ENOENT, ENOTDIR):
                  raise
              # Path doesn't exist or is a broken symlink
              # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
              return False
  2. [Python 3]: з менеджерами контексту заяви . Або:

    • Створіть:

      class Swallow:  # Dummy example
          swallowed_exceptions = (FileNotFoundError,)
      
          def __enter__(self):
              print("Entering...")
      
          def __exit__(self, exc_type, exc_value, exc_traceback):
              print("Exiting:", exc_type, exc_value, exc_traceback)
              return exc_type in Swallow.swallowed_exceptions  # only swallow FileNotFoundError (not e.g. TypeError - if the user passes a wrong argument like None or float or ...)
      • І його використання - я повторюю свою os.path.isfileповедінку (зауважте, що це лише для демонстраційних цілей, не намагайтеся написати такий код для виробництва ):

        import os
        import stat
        
        
        def isfile_seaman(path):  # Dummy func
            result = False
            with Swallow():
                result = stat.S_ISREG(os.stat(path).st_mode)
            return result
    • Використовуйте [Python 3]: contextlib. suppress ( * винятки ) - який був спеціально розроблений для вибіркового придушення винятків


    Але вони, схоже, є обгортками над спробу / за винятком / else / нарешті блокує, як [Python 3]: у заяві вказано:

    Це дозволяє звичайні спроби ... за винятком ..., нарешті, шаблони використання слід капсулювати для зручного використання.

  3. Функції обходу файлової системи (і пошук результатів для відповідності предметів)


    Так як ці ітерації по папках, (в більшості випадків) вони неефективні для нашої задачі (є винятки, наприклад , не символ підстановки Глоб Бінг - в @ShadowRanger вказував), так що я не буду наполягати на них. Не кажучи вже про те, що в деяких випадках може знадобитися обробка імені файлів.

  4. [Пітон 3]: ос. доступ ( шлях, режим, *, dir_fd = Немає, эффективен_ids = Неправдивий, follow_symlinks = True ) , поведінка якого близька os.path.exists(насправді вона ширша, головним чином через 2- й аргумент)

    • дозволи користувача можуть обмежувати "видимість" файлу, як зазначено в документі:

      ... перевірити, чи користувач, що викликає, має вказаний доступ до шляху . режим повинен бути F_OK для перевірки існування шляху ...

    os.access("/tmp", os.F_OK)

    Оскільки я також працюю в C , я також використовую цей метод, тому що під кришкою він викликає нативні API s (знову ж таки, через "$ {PYTHON_SRC_DIR} /Modules/posixmodule.c" ), але він також відкриває ворота для можливого користувача помилки , і це не так Python ic, як інші варіанти. Отже, як справедливо зазначав @AaronHall, не використовуйте його, якщо ви не знаєте, що робите:

    Примітка : виклик рідних API s також можливий через [Python 3]: ctypes - Бібліотека іноземних функцій для Python , але в більшості випадків це складніше.

    ( Win конкретно): Оскільки vcruntime * ( msvcr * ) .dll експортує [MS.Docs]: _access, сімейство функцій _waccess , ось ось приклад:

    Python 3.5.3 (v3.5.3:1880cb95a742, Jan 16 2017, 16:02:32) [MSC v.1900 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import os, ctypes
    >>> ctypes.CDLL("msvcrt")._waccess(u"C:\\Windows\\System32\\cmd.exe", os.F_OK)
    0
    >>> ctypes.CDLL("msvcrt")._waccess(u"C:\\Windows\\System32\\cmd.exe.notexist", os.F_OK)
    -1

    Примітки :

    • Хоча це і не є хорошою практикою, я використовую os.F_OKв дзвінку, але це просто для ясності (його значення дорівнює 0 )
    • Я використовую _waccess, щоб той самий код працював на Python3 і Python2 (незважаючи на відмінності між ними, що стосуються унікоду )
    • Хоча це орієнтоване на дуже специфічну область, вона не згадувалася в жодній з попередніх відповідей


    Також аналог Lnx ( Ubtu (16 x64) ):

    Python 3.5.2 (default, Nov 17 2016, 17:05:23)
    [GCC 5.4.0 20160609] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import os, ctypes
    >>> ctypes.CDLL("/lib/x86_64-linux-gnu/libc.so.6").access(b"/tmp", os.F_OK)
    0
    >>> ctypes.CDLL("/lib/x86_64-linux-gnu/libc.so.6").access(b"/tmp.notexist", os.F_OK)
    -1

    Примітки :

    • Натомість шлях жорсткого кодування libc ( "/lib/x86_64-linux-gnu/libc.so.6" ), який може (і, швидше за все, буде) змінюватись в різних системах, жодна (або порожня рядок) не може бути передана конструктору CDLL ( ctypes.CDLL(None).access(b"/tmp", os.F_OK)). Відповідно до [man7]: DLOPEN (3) :

      Якщо ім'я файлу NULL, то повертається ручка призначена для основної програми. Якщо дано dlsym (), ця ручка викликає пошук символу в основній програмі, після чого всі спільні об'єкти, завантажені при запуску програми, а потім усі спільні об'єкти, завантажені dlopen () з прапором RTLD_GLOBAL .

      • Основна (поточна) програма ( python ) пов'язана з libc , тому її символи (включаючи доступ ) будуть завантажені
      • З цим потрібно поводитися обережно, оскільки такі функції, як main , Py_Main та (всі інші) доступні; виклик їх може мати катастрофічні наслідки (для поточної програми)
      • Це також не стосується Win (але це не така вже й велика справа, оскільки msvcrt.dll розташований у "% SystemRoot% \ System32", який за замовчуванням знаходиться у % PATH% ). Мені хотілося піти далі і повторити цю поведінку на Win (і надіслати патч), але, як виявляється, [MS.Docs]: Функція GetProcAddress "бачить" лише експортовані символи, тому, якщо хтось не оголошує функції в головному виконуваному файлі як __declspec(dllexport)(чому на Землі звичайна людина зробила б це?), основна програма завантажена, але майже непридатна
  5. Встановіть якийсь сторонній модуль із можливостями файлової системи

    Швидше за все, буде покладатися на один із способів, описаних вище (можливо, з невеликими налаштуваннями).
    Одним із прикладів може бути (знову ж таки, Win конкретно) [GitHub]: mhammond / pywin32 - розширення Python для Windows (pywin32) , що є обгорткою Python для WINAPI .

    Але, оскільки це більше схоже на вирішення проблеми, я зупиняюся тут.

  6. Інший (кульгавий) спосіб вирішення ( gainarie ) - це (як я це люблю називати), підхід sysadmin : використовувати Python як обгортку для виконання команд оболонки

    • Виграти :

      (py35x64_test) e:\Work\Dev\StackOverflow\q000082831>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" -c "import os; print(os.system('dir /b \"C:\\Windows\\System32\\cmd.exe\" > nul 2>&1'))"
      0
      
      (py35x64_test) e:\Work\Dev\StackOverflow\q000082831>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" -c "import os; print(os.system('dir /b \"C:\\Windows\\System32\\cmd.exe.notexist\" > nul 2>&1'))"
      1
    • Nix ( Lnx ( Ubtu )):

      [cfati@cfati-ubtu16x64-0:~]> python3 -c "import os; print(os.system('ls \"/tmp\" > /dev/null 2>&1'))"
      0
      [cfati@cfati-ubtu16x64-0:~]> python3 -c "import os; print(os.system('ls \"/tmp.notexist\" > /dev/null 2>&1'))"
      512

Підсумок :

  • Чи є використання спробувати / за винятком / ще / , нарешті , блоків, тому що вони можуть перешкодити вам запуск в серію неприємних проблем. Протиприклад, який я можу придумати, - це ефективність: такі блоки коштують дорого, тому намагайтеся не розміщувати їх у коді, який повинен працювати сотні тисяч разів на секунду (але оскільки (у більшості випадків) це передбачає доступ до диска, це не буде так).

Заключна примітка :

  • Я постараюся бути актуальним, будь-які пропозиції вітаються, я включу все корисне, що з’явиться у відповідь

3
Чи можете ви детальніше розглянути це твердження? "Хоча це не є хорошою практикою, я використовую os.F_OK у дзвінку, але це просто для ясності (його значення 0)"
sk8asd123

6
@ sk8asd123: Начебто важко це додати у коментарі: як правило, найкраще використовувати константи з функціями, з якими вони поєднуються. Це застосовується під час роботи з декількома модулями, які визначають одну і ту ж константу, тому що деякі можуть не бути оновленими, і найкраще, щоб функції та константи синхронізувалися. Під час роботи з ctypes (викликом функцій безпосередньо) я повинен був би визначити константу (від MSDN ) або взагалі не використовувати константу. Це я просто керівництво, яке я використовую, у 99,9% це, ймовірно, не має значення (функціонально).
CristiFati

3
@CristiFati: станом на 3.6, glob.iglobglob.globтак само) базуєтьсяos.scandir , тому зараз лінь; щоб отримати перше звернення до каталогу файлів 10M, ви скануєте лише до першого звернення. І навіть до 3.6, якщо ви використовуєте globметоди без будь-яких символів, ця функція є розумною: вона знає, що ви можете мати лише один удар, тому вона спрощує глобалізацію до справедливої os.path.isdirабоos.path.lexists (залежно від того, чи закінчується шлях /).
ShadowRanger

3
Ця друга частина мого коментаря (недискатульована глобалізація насправді не повторює папку і ніколи не означає), це означає, що це ідеально ефективне рішення проблеми (повільніше, ніж безпосередньо виклик, os.path.isdirабо os.path.lexistоскільки це купа викликів і рядків функції рівня Python Операції до того, як він вирішить, ефективний шлях є життєздатним, але немає додаткового системного виклику або роботи вводу / виводу, що на порядок повільніше).
ShadowRanger

154

Це найпростіший спосіб перевірити наявність файлу. Тільки тому, що файл існував під час перевірки, не гарантує, що він буде там, коли потрібно його відкрити.

import os
fname = "foo.txt"
if os.path.isfile(fname):
    print("file does exist at this time")
else:
    print("no such file exists at this time")

17
Поки ви маєте намір отримати доступ до файлу, умова перегонів існує , незалежно від того, як побудована ваша програма. Ваша програма не може гарантувати, що інший процес на комп'ютері не змінив файл. Це Ерік Ліпперт називає екзогенним винятком . Ви не можете цього уникнути, попередньо перевіривши наявність файлу.
Ісаак Супене

@IsaacSupeene Найкраща практика полягає в тому, щоб зробити вікно операції (файлу) якомога меншим, а потім належним чином обробляти винятки
un33k

145

У Python 3.4+ є об'єктно-орієнтований модуль шляху: pathlib . За допомогою цього нового модуля ви можете перевірити, чи існує такий файл:

import pathlib
p = pathlib.Path('path/to/file')
if p.is_file():  # or p.is_dir() to see if it is a directory
    # do stuff

Ви можете (і зазвичай слід) використовувати try/exceptблок під час відкриття файлів:

try:
    with p.open() as f:
        # do awesome stuff
except OSError:
    print('Well darn.')

Модуль pathlib містить у собі багато цікавих матеріалів: зручне глобулювання, перевірка власника файлу, просте приєднання шляху тощо. Варто перевірити. Якщо ви перебуваєте на більш старій версії Python (версія 2.6 або пізніша версія), ви все одно можете встановити pathlib за допомогою pip:

# installs pathlib2 on older Python versions
# the original third-party module, pathlib, is no longer maintained.
pip install pathlib2

Потім імпортуйте його наступним чином:

# Older Python versions
import pathlib2 as pathlib

124

Віддайте перевагу оператору спробу. Це вважається кращим стилем і уникає перегонів.

Не сприймайте мого слова за це. Існує велика підтримка цієї теорії. Ось пара:


3
Будь ласка, додайте кращі джерела для підтримки вашої заяви.
BlueTrin

11
Цитується посилання "Уникнення перегонів" (підтримка яблучних розробників) не підтримує вашу відповідь. Це стосується лише використання тимчасових файлів, що містять конфіденційну інформацію про погано розроблені операційні системи, які не мають належних завдань тимчасових файлів / каталогів через обмежені дозволи. Використання try...exceptжодного разу не допомагає вирішити цю проблему.
jstine

Проблема цього методу полягає в тому, що якщо у вас є важливий фрагмент коду, залежно від того, який файл не існує, введення його в except:застереження призведе до того, що виняток, що виникає в цій частині вашого коду, призведе до заплутаного повідомлення (друга помилка, піднята під час обробка першого.)
Каміон

119

Як перевірити, чи існує файл, використовуючи Python, не використовуючи оператор спробу?

Тепер, починаючи з Python 3.4, можна імпортувати та інстанціювати Pathоб’єкт з ім'ям файлу та перевірити is_fileметод (зауважте, що це повертає True для символьних посилань, що вказують і на звичайні файли):

>>> from pathlib import Path
>>> Path('/').is_file()
False
>>> Path('/initrd.img').is_file()
True
>>> Path('/doesnotexist').is_file()
False

Якщо ви перебуваєте на Python 2, ви можете підтримати модуль pathlib з pypi pathlib2, або іншим чином перевірити його isfileз os.pathмодуля:

>>> import os
>>> os.path.isfile('/')
False
>>> os.path.isfile('/initrd.img')
True
>>> os.path.isfile('/doesnotexist')
False

Тепер вищесказане, мабуть, найкращий прагматичний прямий відповідь тут, але є можливість перегонового стану (залежно від того, що ви намагаєтеся виконати), а також те, що в основі лежить реалізація try, але Python використовує tryвсюди в своїй реалізації.

Оскільки Python використовується tryскрізь, насправді немає причин уникати реалізації, яка використовує його.

Але решта цієї відповіді намагається розглянути ці застереження.

Більш довга, набагато педантична відповідь

Доступний з Python 3.4, використовуйте новий Pathоб'єкт у pathlib. Зауважте, що .existsце не зовсім правильно, оскільки каталоги - це не файли (за винятком сенсу, що все є файлом).

>>> from pathlib import Path
>>> root = Path('/')
>>> root.exists()
True

Тому нам потрібно використовувати is_file:

>>> root.is_file()
False

Ось допомога на тему is_file:

is_file(self)
    Whether this path is a regular file (also True for symlinks pointing
    to regular files).

Тож давайте отримаємо файл, про який ми знаємо, що це файл:

>>> import tempfile
>>> file = tempfile.NamedTemporaryFile()
>>> filepathobj = Path(file.name)
>>> filepathobj.is_file()
True
>>> filepathobj.exists()
True

За замовчуванням NamedTemporaryFileвидаляє файл, коли він закритий (і автоматично закриється, коли на нього більше не існує посилань).

>>> del file
>>> filepathobj.exists()
False
>>> filepathobj.is_file()
False

Якщо ви скористаєтеся реалізацією , ви побачите, що is_fileвикористовується try:

def is_file(self):
    """
    Whether this path is a regular file (also True for symlinks pointing
    to regular files).
    """
    try:
        return S_ISREG(self.stat().st_mode)
    except OSError as e:
        if e.errno not in (ENOENT, ENOTDIR):
            raise
        # Path doesn't exist or is a broken symlink
        # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
        return False

Умови гонки: Чому нам подобається намагатися

Нам це подобається, tryтому що він уникає перегонів. З try, ви просто намагаєтеся прочитати файл, чекаючи , що вона буде там, і якщо немає, то зловити виняток і виконати всі , що Відкат поведінка має сенс.

Якщо ви хочете перевірити, чи існує файл, перш ніж спробувати його прочитати, і ви можете його видалити, а потім ви можете використовувати кілька потоків або процесів, або інша програма знає про цей файл і може видалити його - ви ризикуєте отримати шанс стан гонки , якщо перевірити його існує, тому що ви тоді мчить , щоб відкрити його до свого стану (його існування) змінюється.

Умови гонки дуже важко налагодити, тому що є дуже маленьке вікно, в якому вони можуть спричинити збій вашої програми.

Але якщо це ваша мотивація, ви можете отримати значення atry твердження за допомогою suppressменеджера контексту.

Уникання перегонів без спроб: suppress

Python 3.4 дає нам suppressменеджер контексту (раніше ignoreменеджер контексту), який робить семантично абсолютно те саме в меншій кількості рядків, а також (принаймні поверхово) зустрічаючись з оригіналом, просить уникнути tryзаяви:

from contextlib import suppress
from pathlib import Path

Використання:

>>> with suppress(OSError), Path('doesnotexist').open() as f:
...     for line in f:
...         print(line)
... 
>>>
>>> with suppress(OSError):
...     Path('doesnotexist').unlink()
... 
>>> 

Для більш ранніх пітонів ви могли розгорнути свій власний suppress, але без tryзаповіту буде більш багатослівним, ніж з. Я вважаю, що це насправді єдина відповідь, яка не використовується tryна будь-якому рівні в Python, до якої можна застосувати до Python 3.4, оскільки замість цього використовується менеджер контексту:

class suppress(object):
    def __init__(self, *exceptions):
        self.exceptions = exceptions
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type is not None:
            return issubclass(exc_type, self.exceptions)

Можливо, простіше: спробуйте:

from contextlib import contextmanager

@contextmanager
def suppress(*exceptions):
    try:
        yield
    except exceptions:
        pass

Інші параметри, які не відповідають запиту "без спроби":

isfile

import os
os.path.isfile(path)

з документів :

os.path.isfile(path)

Повернути True, якщо шлях є існуючим звичайним файлом. Звідси випливає символічне посилання, тому і islink()іisfile() може бути правдою для одного шляху.

Але якщо вивчити джерело цієї функції, ви побачите, що вона насправді використовує спробуйте:

# This follows symbolic links, so both islink() and isdir() can be true
# for the same path on systems that support symlinks
def isfile(path):
    """Test whether a path is a regular file"""
    try:
        st = os.stat(path)
    except os.error:
        return False
    return stat.S_ISREG(st.st_mode)
>>> OSError is os.error
True

Все, що він робить, - це використовувати заданий шлях, щоб побачити, чи може він отримати статистику по ньому, ловити OSError та перевіривши, чи це файл, якщо він не підняв виняток.

Якщо ви збираєтесь зробити щось із файлом, я б запропонував безпосередньо спробувати його, за винятком випадків, щоб уникнути перегонів:

try:
    with open(path) as f:
        f.read()
except OSError:
    pass

ос.доступ

Доступно для Unix і Windows є os.access, але для їх використання потрібно передавати прапори, і це не розрізняє файли та каталоги. Це більше використовується для перевірки, чи справжній користувач, що викликає доступ, має доступ у середовищі підвищених привілеїв:

import os
os.access(path, os.F_OK)

Він також страждає від тих же проблем зі станом раси, що і isfile. З документів :

Примітка: Використання доступу () для перевірки того, чи користувач має право, наприклад, відкривати файл, перш ніж це зробити, використовуючи open (), створює отвір у захисті, оскільки користувач може використовувати короткий проміжок часу між перевіркою та відкриттям файлу для маніпулювання ним. Переважно використовувати методи EAFP. Наприклад:

if os.access("myfile", os.R_OK):
    with open("myfile") as fp:
        return fp.read()
return "some default data"

краще писати так:

try:
    fp = open("myfile")
except IOError as e:
    if e.errno == errno.EACCES:
        return "some default data"
    # Not a permission error.
    raise
else:
    with fp:
        return fp.read()

Уникайте використання os.access . Це функція низького рівня, яка має більше можливостей для помилок користувача, ніж обговорювані вище об'єкти та функції.

Критика іншої відповіді:

Ще одна відповідь говорить про це os.access:

Особисто я віддаю перевагу цьому, тому що під кришкою він називає нативні API (через "$ {PYTHON_SRC_DIR} /Modules/posixmodule.c"), але він також відкриває ворота для можливих помилок користувача, і це не так Pythonic, як інші варіанти :

У цій відповіді сказано, що він надає перевагу непітонічному методу, схильному до помилок, без виправдання. Здається, це заохочує користувачів використовувати API низького рівня, не розуміючи їх.

Він також створює контекстний менеджер, який, безумовно, повертаючись True, дозволяє всі винятки (включаючи KeyboardInterruptіSystemExit !) Пройти безшумно, що є хорошим способом приховати помилки.

Це, мабуть, спонукає користувачів до прийняття поганої практики.


87
import os
#Your path here e.g. "C:\Program Files\text.txt"
#For access purposes: "C:\\Program Files\\text.txt"
if os.path.exists("C:\..."):   
    print "File found!"
else:
    print "File not found!"

Імпорт osполегшує навігацію та виконання стандартних дій із операційною системою.

Для ознайомлення також див. Як перевірити, чи існує файл за допомогою Python?

Якщо вам потрібні операції високого рівня, використовуйте shutil.


9
Ця відповідь неправильна. os.path.existsповертає true для речей, які не є файлами, наприклад, каталогів. Це дає помилкові позитиви. Дивіться інші відповіді, які рекомендують os.path.isfile.
Кріс Джонсон

84

Тестування файлів і папок з os.path.isfile(), os.path.isdir()іos.path.exists()

Припускаючи, що "шлях" є дійсним шляхом, у цій таблиці показано, що повертається кожною функцією для файлів і папок:

введіть тут опис зображення

Ви також можете перевірити, чи файл - це певний тип файлів, використовуючи os.path.splitext()для отримання розширення (якщо ви цього ще не знаєте)

>>> import os
>>> path = "path to a word document"
>>> os.path.isfile(path)
True
>>> os.path.splitext(path)[1] == ".docx" # test if the extension is .docx
True

72

У 2016 році найкращий спосіб все ще використовується os.path.isfile:

>>> os.path.isfile('/path/to/some/file.txt')

Або в Python 3 ви можете використовувати pathlib:

import pathlib
path = pathlib.Path('/path/to/some/file.txt')
if path.is_file():
    ...

3
Чи можу я запитати: Яка перевага використання модуля 'pathlib' замість модуля 'os' в python3 для цієї перевірки?
Джоко

3
pathlibє рішенням OOP python для шляхів. З цим можна зробити набагато більше. Якщо вам просто потрібно перевірити існування, перевага не така велика.
KaiBuxe

65

Мабуть, не існує змістовної функціональної різниці між спробою / за винятком і isfile() , тому слід використовувати те, що має сенс.

Якщо ви хочете прочитати файл, якщо він існує, зробіть це

try:
    f = open(filepath)
except IOError:
    print 'Oh dear.'

Але якщо ви просто хотіли перейменувати файл, якщо він існує, і тому його не потрібно відкривати, зробіть це

if os.path.isfile(filepath):
    os.rename(filepath, filepath + '.old')

Якщо ви хочете записати у файл, якщо він не існує, зробіть це

# python 2
if not os.path.isfile(filepath):
    f = open(filepath, 'w')

# python 3, x opens for exclusive creation, failing if the file already exists
try:
    f = open(filepath, 'wx')
except IOError:
    print 'file already exists'

Якщо вам потрібно заблокувати файл, це вже інша справа.


3
Ця відповідь неправильна. os.path.existsповертає true для речей, які не є файлами, наприклад, каталогів. Це дає помилкові позитиви. Дивіться інші відповіді, які рекомендують os.path.isfile.
Кріс Джонсон

6
У вашому третьому прикладі я створюю посилання, назване filepathправильним часом, і BAM , ви перезаписуєте цільовий файл. Ви повинні робити open(filepath, 'wx')це try...exceptблоком, щоб уникнути проблеми.
спектри

1
У вашому другому прикладі, принаймні в Windows, ви отримаєте, OSErrorякщо він filepath + '.old'вже існує: "У Windows, якщо dst вже існує, OSError буде піднятий, навіть якщо це файл; можливо, немає можливості реалізувати атомну перейменування, коли dst називає існуючий файл. "
Том Мидделтин

@TomMyddeltyn: Станом на Python 3.3,os.replace портативно виконує безшумну заміну файлу призначення (він ідентичний os.renameповедінці Linux) (він помиляється лише в тому випадку, якщо ім'я призначення є і є каталогом). Таким чином, ви застрягли на 2.x, але користувачі Py3 мали хороший варіант вже кілька років.
ShadowRanger

На renameприкладі: все одно це слід робити з try/ except. os.rename(або os.replaceна сучасному Python) є атомним; перевіряючи, тоді перейменування вводить непотрібну гонку та додаткові системні виклики. Просто зробітьtry: os.replace(filepath, filepath + '.old') except OSError: pass
ShadowRanger

59

Ви можете спробувати це (безпечніше):

try:
    # http://effbot.org/zone/python-with-statement.htm
    # 'with' is safer to open a file
    with open('whatever.txt') as fh:
        # Do something with 'fh'
except IOError as e:
    print("({})".format(e))

Вихід буде:

([Errno 2] Немає такого файлу чи каталогу: "будь-який.txt")

Тоді, залежно від результату, ваша програма може просто продовжувати працювати звідти, або ви можете ввести код, щоб зупинити її, якщо хочете.


18
Оригінальний питання просив рішення , яке не використовуєtry
РРП

5
Ця відповідь пропускає пункт ОП. Перевірка наявності файлу - це не те саме, що перевірка, чи можете ви відкрити його. Будуть випадки, коли файл існує, але з різних причин ви не можете його відкрити.
Кріс Джонсон

51

Хоча я завжди рекомендую використовувати tryта exceptзаяви, але для вас є декілька можливостей (моя особиста фаворитка - це використання os.access):

  1. Спробуйте відкрити файл:

    Відкриття файлу завжди підтвердить існування файлу. Ви можете зробити функцію так само:

    def File_Existence(filepath):
        f = open(filepath)
        return True

    Якщо це False, він зупинить виконання з безмежною IOError або OSError в пізніших версіях Python. Щоб зловити виняток, вам потрібно використати спробу, за винятком пункту. Звичайно, ви завжди можете використовувати за tryвинятком оператора `if '(завдяки hsandt за те, що я задумався):

    def File_Existence(filepath):
        try:
            f = open(filepath)
        except IOError, OSError: # Note OSError is for later versions of Python
            return False
    
        return True
  2. Використання os.path.exists(path):

    Це дозволить перевірити існування того, що ви вказали. Однак він перевіряє наявність файлів та каталогів, тому будьте обережні, як ви ним користуєтесь.

    import os.path
    >>> os.path.exists("this/is/a/directory")
    True
    >>> os.path.exists("this/is/a/file.txt")
    True
    >>> os.path.exists("not/a/directory")
    False
  3. Використання os.access(path, mode):

    Це дозволить перевірити, чи є у вас доступ до файлу. Він перевірить наявність дозволів. На основі документації на os.py, ввівши os.F_OKїї, вона перевірить існування шляху. Однак, використовуючи це, ви створите отвір у захисті, оскільки хтось може атакувати ваш файл, використовуючи час між перевіркою дозволів та відкриттям файлу. Натомість слід перейти безпосередньо до відкриття файлу, а не перевіряти його дозволи. ( EAFP проти LBYP ). Якщо ви не збираєтеся потім відкривати файл і лише перевіряти його існування, ви можете скористатися цим.

    У будь-якому випадку, тут:

    >>> import os
    >>> os.access("/is/a/file.txt", os.F_OK)
    True

Я також повинен зазначити, що є два способи, за якими ви не зможете перевірити існування файлу. Або питання буде, permission deniedабо no such file or directory. Якщо ви ловите IOError, встановіть IOError as e(як, наприклад, мій перший варіант) та введіть текст, print(e.args)щоб можна було сподіватися визначити свою проблему. Я сподіваюся, що це допомагає! :)


51

Дата: 2017-12-04

Кожне можливе рішення було перелічено в інших відповідях.

Інтуїтивно зрозумілий і аргументований спосіб перевірити наявність файлу є наступним:

import os
os.path.isfile('~/file.md')  # Returns True if exists, else False
# additionaly check a dir
os.path.isdir('~/folder')  # Returns True if the folder exists, else False
# check either a dir or a file
os.path.exists('~/file')

Я зробив вичерпний чіт-лист для вашої довідки:

#os.path methods in exhaustive cheatsheet
{'definition': ['dirname',
               'basename',
               'abspath',
               'relpath',
               'commonpath',
               'normpath',
               'realpath'],
'operation': ['split', 'splitdrive', 'splitext',
               'join', 'normcase'],
'compare': ['samefile', 'sameopenfile', 'samestat'],
'condition': ['isdir',
              'isfile',
              'exists',
              'lexists'
              'islink',
              'isabs',
              'ismount',],
 'expand': ['expanduser',
            'expandvars'],
 'stat': ['getatime', 'getctime', 'getmtime',
          'getsize']}

37

Якщо файл призначений для відкриття, ви можете скористатися однією з таких методик:

with open('somefile', 'xt') as f: #Using the x-flag, Python3.3 and above
    f.write('Hello\n')

if not os.path.exists('somefile'): 
    with open('somefile', 'wt') as f:
        f.write("Hello\n")
else:
    print('File already exists!')

ОНОВЛЕННЯ

Тільки, щоб уникнути плутанини та на основі отриманих відповідей, поточна відповідь знаходить або файл, або каталог із заданим іменем.


9
Ця відповідь неправильна. os.path.existsповертає true для речей, які не є файлами, наприклад, каталогів. Це дає помилкові позитиви. Дивіться інші відповіді, які рекомендують os.path.isfile.
Кріс Джонсон

також отримали помилково позитивну проблему.
Zorglub29

docs.python.org/3/library/os.path.html#os.path.exists До вищезазначеного твердження від chris >> os.path.exists (path)> Return True, якщо шлях посилається на існуючий шлях або відкритий дескриптор файлу. Повертається помилковим для розірваних символічних посилань. На деяких платформах ця функція може повернути False, якщо не буде надано дозвіл на виконання os.stat () на запитуваному файлі, навіть якщо шлях фізично існує. Змінено у версії 3.3: шлях тепер може бути цілим числом: True повертається, якщо це дескриптор відкритого файлу, помилково неправильно. Змінено у версії 3.6: приймає об’єкт, що нагадує шлях.
JayRizzo

36

Додатково os.access():

if os.access("myfile", os.R_OK):
    with open("myfile") as fp:
        return fp.read()

Будучи R_OK, W_OKі X_OKпрапори для тестування дозволів ( документ ).


20
if os.path.isfile(path_to_file):
    try: 
        open(path_to_file)
            pass
    except IOError as e:
        print "Unable to open file"

Збільшення винятків вважається прийнятним та пітонічним підходом до контролю потоку у вашій програмі. Подумайте про обробку відсутніх файлів за допомогою IOErrors. У цій ситуації виняток IOError буде піднято, якщо файл існує, але користувач не має дозволів на читання.

SRC: http://www.pfinn.net/python-check-if-file-exists.html


3
ОП запитала, як перевірити, чи існує файл. Файл може існувати, але ви не зможете його відкрити. Тому використання відкриття файлу як проксі для перевірки, чи файл існує невірно: буде помилковим негативом.
Кріс Джонсон

19

Якщо ви імпортували NumPy вже для інших цілей , то немає необхідності імпортувати інші бібліотеки , такі як pathlib, os, pathsі т.д.

import numpy as np
np.DataSource().exists("path/to/your/file")

Це повернеться справжнім або хибним на основі його існування.


18

Можна написати пропозицію Брайана без цього try:.

from contextlib import suppress

with suppress(IOError), open('filename'):
    process()

suppressє частиною Python 3.4. У старих випусках ви можете швидко написати власне подавлення:

from contextlib import contextmanager

@contextmanager
def suppress(*exceptions):
    try:
        yield
    except exceptions:
        pass

17

Я автор пакету, який існує близько 10 років, і він має функцію, яка безпосередньо стосується цього питання. В основному, якщо ви працюєте в системі, яка не є системою Windows, вона використовує Popenдля доступу find. Однак якщо ви працюєте в Windows, він реплікується findза допомогою ефективної прогулки файлової системи.

Сам код не використовує tryблок… за винятком визначення операційної системи і, таким чином, керування вами до стилю «Unix» findабо hand-buillt find. Тести на встановлення термінів показали, що tryшвидкість у визначенні ОС була швидшою, тому я там її застосував (але більше ніде).

>>> import pox
>>> pox.find('*python*', type='file', root=pox.homedir(), recurse=False)
['/Users/mmckerns/.python']

І док ...

>>> print pox.find.__doc__
find(patterns[,root,recurse,type]); Get path to a file or directory

    patterns: name or partial name string of items to search for
    root: path string of top-level directory to search
    recurse: if True, recurse down from root directory
    type: item filter; one of {None, file, dir, link, socket, block, char}
    verbose: if True, be a little verbose about the search

    On some OS, recursion can be specified by recursion depth (an integer).
    patterns can be specified with basic pattern matching. Additionally,
    multiple patterns can be specified by splitting patterns with a ';'
    For example:
        >>> find('pox*', root='..')
        ['/Users/foo/pox/pox', '/Users/foo/pox/scripts/pox_launcher.py']

        >>> find('*shutils*;*init*')
        ['/Users/foo/pox/pox/shutils.py', '/Users/foo/pox/pox/__init__.py']

>>>

Реалізація, якщо ви хочете подивитися, тут: https://github.com/uqfoundation/pox/blob/89f90fb308f285ca7a62eabe2c38acb87e89dad9/pox/shutils.py#L190


17

Перевірте наявність файлу чи каталогу

Ви можете дотримуватися цих трьох способів:

Примітка1: os.path.isfileвикористовується лише для файлів

import os.path
os.path.isfile(filename) # True if file exists
os.path.isfile(dirname) # False if directory exists

Примітка2: os.path.existsвикористовується як для файлів, так і для каталогів

import os.path
os.path.exists(filename) # True if file exists
os.path.exists(dirname) #True if directory exists

pathlib.PathМетод (включено в Python 3 +, що встановлюється з Піп для Python 2)

from pathlib import Path
Path(filename).exists()

16

Додаємо ще одну незначну варіацію, яка не відображена точно в інших відповідях.

Це стосується випадку file_pathбуття Noneабо порожнього рядка.

def file_exists(file_path):
    if not file_path:
        return False
    elif not os.path.isfile(file_path):
        return False
    else:
        return True

Додавання варіанту на основі пропозиції Шахбаза

def file_exists(file_path):
    if not file_path:
        return False
    else:
        return os.path.isfile(file_path)

Додавання варіанту на основі пропозиції Пітера Вуда

def file_exists(file_path):
    return file_path and os.path.isfile(file_path):

3
if (x) return true; else return false;насправді просто return x. Вашими останніми чотирма рядками можуть стати return os.path.isfile(file_path). Поки ми працюємо, всю функцію можна спростити як return file_path and os.path.isfile(file_path).
Шахбаз

Ви повинні бути обережними return xу випадку if (x). Python вважатиме порожню рядок False, і в цьому випадку ми повертаємо порожню рядок замість bool. Мета цієї функції - завжди повертати bool.
Марсель Вілсон

1
Правда. У цьому випадку , однак, xце os.path.isfile(..)так уже BOOL.
Шахбаз

os.path.isfile(None)викликає виняток, тому я додав прапорець if. Я, ймовірно, міг би просто загорнути його в спробу / крім цього, але я вважав, що це було більш чітко.
Марсель Вілсон

3
return file_path and os.path.isfile(file_path)
Пітер Вуд

15

Ось команда Python на 1 рядок для середовища командного рядка Linux. Я знаходжу це ДУЖЕ РОЧУ, оскільки я не такий гарячий хлопець Баша.

python -c "import os.path; print os.path.isfile('/path_to/file.xxx')"

Я сподіваюся, що це корисно.


6
Однорядкова перевірка в bash: [ -f "${file}" ] && echo "file found" || echo "file not found"(що таке саме if [ ... ]; then ...; else ...; fi).
flotzilla

12

Ви можете використовувати бібліотеку "ОС" Python:

>>> import os
>>> os.path.exists("C:\\Users\\####\\Desktop\\test.txt") 
True
>>> os.path.exists("C:\\Users\\####\\Desktop\\test.tx")
False

5
Ця відповідь неправильна. os.path.existsповертає true для речей, які не є файлами, наприклад, каталогів. Це дає помилкові позитиви. Дивіться інші відповіді, які рекомендують os.path.isfile.
Кріс Джонсон

@Chris Johnson, os.path.exists () функція перевіряє, чи існує шлях у системі. PATH може бути ДИРЕКТОРІЮ або ФАЙЛОМ. Це буде добре працювати в обох випадках. Спробуйте, будь ласка, з прикладом
Pradip Das

Отже, ця відповідь працює. Чудово. Якщо шлях не є файлом. Це те, про що йшлося? №
Дебосміт Рей

Це залежить. Якщо метою визначення існування "файла" є з'ясування того, чи існує вже шлях (і, отже, це не шлях, де нові дані можуть зберігатися без видалення іншої інформації), то existsце добре. Якщо мета - визначити, чи безпечно відкрити імовірно існуючий файл, то критика виправдана і існує недостатньо точно. На жаль, ОП не вказує, яка є бажана мета (і, мабуть, більше не зробить цього).
starturtle

12

Як перевірити, чи існує файл, не використовуючи оператор спробу?

У 2016 році це, мабуть, найпростіший спосіб перевірити, чи існує обидва файли та чи є він файлом:

import os
os.path.isfile('./file.txt')    # Returns True if exists, else False

isfileнасправді є лише допоміжним методом, який використовується внутрішньо os.statі stat.S_ISREG(mode)під ним. Це os.statметод нижчого рівня, який надасть вам детальну інформацію про файли, каталоги, сокети, буфери тощо. Більше про os.stat тут

Примітка: Однак такий підхід жодним чином не заблокує файл, і тому ваш код може стати вразливим до помилок " час перевірки на час використання " ( TOCTTOU ).

Тому підвищення винятків вважається прийнятним та пітонічним підходом до контролю потоку у вашій програмі. І слід розглянути питання про обробку відсутніх файлів за допомогою IOErrors, а не ifтвердження ( лише порада ).


9
import os.path

def isReadableFile(file_path, file_name):
    full_path = file_path + "/" + file_name
    try:
        if not os.path.exists(file_path):
            print "File path is invalid."
            return False
        elif not os.path.isfile(full_path):
            print "File does not exist."
            return False
        elif not os.access(full_path, os.R_OK):
            print "File cannot be read."
            return False
        else:
            print "File can be read."
            return True
    except IOError as ex:
        print "I/O error({0}): {1}".format(ex.errno, ex.strerror)
    except Error as ex:
        print "Error({0}): {1}".format(ex.errno, ex.strerror)
    return False
#------------------------------------------------------

path = "/usr/khaled/documents/puzzles"
fileName = "puzzle_1.txt"

isReadableFile(path, fileName)

@ j6m8 так, isReadableFile(path,fileName)повернеться, Trueякщо файл буде доступний і
читабений
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.