在Python中以相对位置打开文件

212

假设我的Python代码在名为main的目录中执行,应用程序需要访问main/2091/data.txt

我应该如何使用 open(location) ? 参数location应该是什么?

我发现下面这个简单的代码可以工作...它有什么缺点吗?

file = "\2091\sample.txt"
path = os.getcwd()+file
fp = open(path, 'r+');

5
你正在使用未转义的反斜杠,这是一个缺点。 - orip
8
几个缺点。1)按照@orip的建议,即使在Windows上也要使用斜杠来表示路径。否则,您的字符串将无法工作。或者像 r"\2091\sample.txt" 这样使用原始字符串。或者转义它们,如 "\\2091\\sample.txt"(但这很烦人)。还有2)您正在使用getcwd(),它是在执行脚本时所在的路径。我以为您想要相对于脚本位置(但现在不确定了)。3)始终使用os.path函数操作路径。您的路径拼接行应该是os.path.join(os.getcwd(), file) 。4)分号是没有意义的。 - Russ
4
为了保险起见...5)使用上下文管理器来使代码更整洁,并避免忘记关闭文件:with open(path, 'r+') as fp:。点击这里查看我看过的最好的with语句解释。 - Russ
1
除了刚才提到的必要斜杠注意事项外,还有一个函数os.path.abspath可以轻松获取相对路径打开的完整路径。最终语句看起来像这样:os.path.abspath('./2091/sample.txt') - OPMendeavor
14个回答

274

在这种情况下,你需要注意你实际的工作目录是什么。例如,你可能没有从文件所在的目录运行脚本。在这种情况下,你不能仅使用相对路径。

如果你确定想要的文件在脚本实际位于的子目录中,你可以使用 __file__ 来帮助解决问题。__file__ 是正在运行的脚本所在位置的完整路径。

因此,你可以使用类似以下代码:

import os
script_dir = os.path.dirname(__file__) #<-- absolute dir the script is in
rel_path = "2091/data.txt"
abs_file_path = os.path.join(script_dir, rel_path)

我发现下面这段简单的代码可以工作,它有什么缺点吗?<pre> file="\sample.txt" path=os.getcwd()+str(loc)+file fp=open(path,'r+');<code> - user845459
@Arash 这样做的缺点是 cwd(当前工作目录)可能是任何东西,并不一定是你的脚本所在的位置。 - Cory Mawhorter
5
__file__ 是一个相对路径(至少在我的设置中是这样,出于某种原因),你需要先调用 os.path.abspath(__file__)。 osx/homebrew 2.7 - Cory Mawhorter
3
在Python 2.7中,os.path.dirname(file)无法正常工作。它会显示NameError:name '__file __'未定义 - Soumendra
1
@Soumendra 我认为你正在控制台中尝试。请在*.py文件中尝试。 - Enkum
1
你不应该使用os.path.join来处理2091/data.txt吗?在Windows系统中,使用\\/会导致问题,不是吗? - FLAW

53

这段代码运行良好:

import os

def read_file(file_name):
    file_handle = open(file_name)
    print file_handle.read()
    file_handle.close()

file_dir = os.path.dirname(os.path.realpath('__file__'))
print file_dir

#For accessing the file in the same folder
file_name = "same.txt"
read_file(file_name)

#For accessing the file in a folder contained in the current folder
file_name = os.path.join(file_dir, 'Folder1.1/same.txt')
read_file(file_name)

#For accessing the file in the parent folder of the current folder
file_name = os.path.join(file_dir, '../same.txt')
read_file(file_name)

#For accessing the file inside a sibling folder.
file_name = os.path.join(file_dir, '../Folder2/same.txt')
file_name = os.path.abspath(os.path.realpath(file_name))
print file_name
read_file(file_name)

对我来说,访问当前文件夹的父文件夹中的文件没有起作用... ".." 被添加为字符串。 - M. Paul
没有在Windows上工作。文件路径正确,但Python状态“文件未找到”,并显示带有\分隔符的路径。 - lonstar
3
在Ubuntu和Python3中,我必须从'__file__'中去掉引号,这样就变成了:fileDir = os.path.dirname(os.path.realpath(__file__)) - pchtsp
1
为避免使用 / 或 \,你可以使用 os.path.join(path,'folderA','file.txt') - Mauricio Gracia Gutierrez
1
这是 __file__ 不是 '__file__' - Marius

34

我创建了一个账户,只是为了澄清我认为在Russ原始回答中发现的差异。

作为参考,他原始回答如下:

import os
script_dir = os.path.dirname(__file__)
rel_path = "2091/data.txt"
abs_file_path = os.path.join(script_dir, rel_path)

这是一个很好的答案,因为它试图动态创建所需文件的绝对系统路径。

Cory Mawhorter注意到__file__是一个相对路径(在我的系统上也是如此),并建议使用os.path.abspath(__file__)。 然而,os.path.abspath返回的是你当前脚本的绝对路径(即/path/to/dir/foobar.py

要使用这种方法(以及我最终让它工作的方式),您必须从路径末尾删除脚本名称:

import os
script_path = os.path.abspath(__file__) # i.e. /path/to/dir/foobar.py
script_dir = os.path.split(script_path)[0] #i.e. /path/to/dir/
rel_path = "2091/data.txt"
abs_file_path = os.path.join(script_dir, rel_path)

在这个例子中,最终的abs_file_path为:/path/to/dir/2091/data.txt


23
甚至可以结合这两种方法来简化os.path.dirname(os.path.abspath(__file__)) - Luke Taylor
3
@LukeTaylor 确实,这比尝试自己复制os.path.dirname的功能要好,就像我去年在回答中所做的那样。 - Grant Hulegaard

28

这取决于你使用的操作系统。如果你想要一个适用于Windows和*nix的解决方案,可以尝试以下方式:

from os import path

file_path = path.relpath("2091/data.txt")
with open(file_path) as f:
    <do stuff>

应该可以正常工作。

path模块能够格式化适用于当前操作系统的路径。此外,只要有正确的权限,Python 就可以处理相对路径。

编辑:

如评论中kindall所提到的那样,Python 可以在unix风格和windows风格之间进行转换,因此甚至更简单的代码也可以正常工作:

with open("2091/data/txt") as f:
    <do stuff>

话虽如此,path 模块仍具有一些有用的函数。


5
relpath() 函数将一个路径名转换为相对路径。由于它已经是一个相对路径,所以它不会做任何事情。 - kindall
如果适用,它将把Unix风格的路径转换为Windows风格的路径。os.path模块中是否有另一个更好的选择? - Wilduck
1
Windows已经可以很好地处理UNIX风格的路径了,至少NT系列(2000、XP、Vista、7)可以。不需要进行任何转换。 - kindall
7
这个答案并不完全正确,可能会引起问题。 相对路径默认相对于当前工作目录(脚本执行的路径),而不是实际脚本位置。 您需要使用__file__。 请参阅我的答案。 - Russ
这个回答的作者是否混淆了os.path.relpath和os.path.abspath? - foobarbecue

22

我花了很多时间在Windows系统上运行Python 3时,发现我的代码无法找到文件的原因。所以我在斜杠前面加了一个点,然后一切都正常了:

import os

script_dir = os.path.dirname(__file__)
file_path = os.path.join(script_dir, './output03.txt')
print(file_path)
fptr = open(file_path, 'w')

1
更好的写法:file_path = os.path.join(script_dir, 'output03.txt') - Mr_and_Mrs_D
1
我在Windows操作系统上尝试过,但没有成功。 - Ângelo Polotto
有趣 - 你能打印 script_dir 吗?然后将其转换为绝对路径,如 script_dir = os.path.abspath(os.path.dirname(__file__)) - Mr_and_Mrs_D
我会尝试,如果成功了,我会更改答案。 - Ângelo Polotto

14

试试这个:

from pathlib import Path

data_folder = Path("/relative/path")
file_to_open = data_folder / "file.pdf"

f = open(file_to_open)

print(f.read())

Python 3.4 引入了一个处理文件和路径的新标准库,称为 pathlib。对我来说很有用!


请使用with data_folder.open() as f替代原有的方法。 - Neuron

7

代码:

import os
script_path = os.path.abspath(__file__) 
path_list = script_path.split(os.sep)
script_directory = path_list[0:len(path_list)-1]
rel_path = "main/2091/data.txt"
path = "/".join(script_directory) + "/" + rel_path

解释:

导入库:

import os

使用__file__获取当前脚本的路径:

script_path = os.path.abspath(__file__)

将脚本路径分成多个项目:

path_list = script_path.split(os.sep)

从列表中删除最后一项(实际脚本文件):
script_directory = path_list[0:len(path_list)-1]

添加相关文件的路径:

rel_path = "main/2091/data.txt

将列表项组合起来,并添加相对路径下的文件:

path = "/".join(script_directory) + "/" + rel_path

现在您已经准备好对文件进行任何操作,例如:
file = open(path)

1
你应该使用os模块,而不是path = "/".join(script_directory) + "/" + rel_path,例如path = os.path.join(script_directory, rel_path)。你应该使用script_path = os.path.dirname(__file__)来代替手动解析路径。 - Mr_and_Mrs_D

4
import os
def file_path(relative_path):
    dir = os.path.dirname(os.path.abspath(__file__))
    split_path = relative_path.split("/")
    new_path = os.path.join(dir, *split_path)
    return new_path

with open(file_path("2091/data.txt"), "w") as f:
    f.write("Powerful you have become.")

3
获取父文件夹的路径,然后使用os.join将您的相对文件添加到结尾。
# get parent folder with `os.path`
import os.path

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

# now use BASE_DIR to get a file relative to the current script
os.path.join(BASE_DIR, "config.yaml")

同样的事情也可以使用 pathlib 实现:
# get parent folder with `pathlib`'s Path
from pathlib import Path

BASE_DIR = Path(__file__).absolute().parent

# now use BASE_DIR to get a file relative to the current script
BASE_DIR / "config.yaml"

所有大写的名称都应保留为常量。 - Neuron
在这种情况下,BASE_DIR不是一个常量吗?运行的脚本的父文件夹在运行时不会改变。 - James Wong

3
如果文件位于您的父文件夹中,例如 follower.txt,您可以简单地使用 open('../follower.txt', 'r').read() 来读取该文件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接