Как проверить, существует ли файл без исключений? - PullRequest
5031 голосов
/ 17 сентября 2008

Как узнать, существует файл или нет, без использования оператора try?

Ответы [ 38 ]

110 голосов
/ 11 августа 2015

Как проверить, существует ли файл с использованием Python без оператора try?

Теперь доступно с 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 вы просто пытаетесь прочитать свой файл, ожидая, что он там будет, а если нет, вы перехватываете исключение и выполняете любое резервное поведение, которое имеет смысл.

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

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

Но если это ваша мотивация, вы можете получить значение оператора try с помощью диспетчера контекста 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() могут быть истинными для одного и того же пути.

Но если вы изучите источник этой функции, вы увидите, что она действительно использует оператор try:

# 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

os.access

Для Unix и Windows доступно os.access, но для использования вы должны пройти флаги, и это не делает различий между файлами и каталогами. Это больше используется для проверки, имеет ли реальный вызывающий пользователь доступ в среде с повышенными привилегиями:

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

Он также страдает от тех же проблем с расой, что и isfile. Из документов :

Примечание: Использование access () для проверки того, авторизован ли пользователь, например, открыть файл прежде чем сделать это с помощью 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!) Проходить без вывода сообщений, что является хорошим способом скрыть ошибки.

Похоже, это поощряет пользователей применять плохую практику.

82 голосов
/ 25 мая 2015
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.

76 голосов
/ 08 октября 2016

Тестирование для файлов и папок с os.path.isfile(), os.path.isdir() и os.path.exists()

Предполагая, что «путь» является допустимым путем, эта таблица показывает, что возвращается каждой функцией для файлов и папок:

enter image description here

Вы также можете проверить, является ли файл файлом определенного типа, используя 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
67 голосов
/ 24 февраля 2016

В 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():
    ...
62 голосов
/ 25 сентября 2013

Не похоже, что между try / исключением и 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'

Если вам нужна блокировка файла, это другое дело.

53 голосов
/ 26 января 2011

Вы можете попробовать это (безопаснее):

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] Нет такого файла или каталога: 'Whatever.txt')

Затем, в зависимости от результата, ваша программа может просто продолжать работать оттуда, или вы можете написать код, чтобы остановить его, если хотите.

48 голосов
/ 26 декабря 2014

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

  1. Попробуйте открыть файл:

    Открытие файла всегда проверяет наличие файла. Вы можете сделать функцию так:

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

    Если значение равно False, выполнение будет остановлено с неуправляемой ошибкой ввода-вывода или OSError в более поздних версиях Python. Чтобы поймать исключение, Вы должны использовать попытку, кроме пункта. Конечно, вы всегда можете используйте оператор try кроме`, как это делается (спасибо 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), чтобы вы могли надеяться определить вашу проблему. Я надеюсь, что это помогает! :)

42 голосов
/ 04 декабря 2017

Дата: 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']}
33 голосов
/ 17 сентября 2008

Дополнительно os.access():

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

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

30 голосов
/ 13 октября 2014

Если файл предназначен для открытия, вы можете использовать один из следующих методов:

>>> 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!')

UPDATE

Просто чтобы избежать путаницы и основываясь на полученных мной ответах, текущий ответ находит либо файл , либо каталог с заданным именем.

...