为啥 os.path.join() 在这种情况下不起作用?
Posted
技术标签:
【中文标题】为啥 os.path.join() 在这种情况下不起作用?【英文标题】:Why doesn't os.path.join() work in this case?为什么 os.path.join() 在这种情况下不起作用? 【发布时间】:2010-12-29 02:10:00 【问题描述】:下面的代码不会加入,调试时该命令不存储整个路径,而只存储最后一个条目。
os.path.join('/home/build/test/sandboxes/', todaystr, '/new_sandbox/')
当我测试它时,它只存储代码的/new_sandbox/
部分。
【问题讨论】:
【参考方案1】:后面的字符串不应以斜杠开头。如果它们以斜线开头,则它们被视为“绝对路径”,并且它们之前的所有内容都将被丢弃。
引用Python docs for os.path.join
:
如果一个组件是绝对路径,所有之前的组件都被丢弃,并从绝对路径组件继续加入。
注意在 Windows 上,与驱动器号相关的行为,与早期的 Python 版本相比似乎发生了变化:
在 Windows 上,当遇到绝对路径组件(例如,
r'\foo'
)时,不会重置驱动器号。如果组件包含驱动器号,则所有以前的组件都将被丢弃并重置驱动器号。请注意,由于每个驱动器都有一个当前目录,os.path.join("c:", "foo")
表示相对于驱动器C:
(c:foo
) 上当前目录的路径,而不是c:\foo
。
【讨论】:
-1:No 字符串应包含“/”。 os.path.join 的一个重点是防止在路径中放置任何斜杠。 str.join() 的问题当然是它不能消除双斜线。我认为这是使用 os.path.join 的人们的主要目的。例如'/'.join(['/etc/', '/conf']) 产生三个斜杠:'/etc///conf' @DustinRasener 您可以使用os.path.normpath
来实现该目标。
不知道为什么人们对 os.path.join 行为感到沮丧。在其他语言中,等效的路径连接库/方法的行为完全相同。它更安全,也更有意义。
这令人沮丧,因为它是隐式魔法,与“显式胜于隐式”的cardinal heuristic 相反。它是。语言设计者可能认为他们知道得更清楚,但存在明显且明显安全的理由偶尔想要这样做。现在我们不能。这就是为什么我们不能拥有美好的事物。【参考方案2】:
os.path.join()
的想法是让你的程序跨平台(linux/windows/etc)。
即使是一个斜线也会破坏它。
所以只有在与某种参考点一起使用时才有意义,例如
os.environ['HOME']
或 os.path.dirname(__file__)
。
【讨论】:
相对路径中的前置斜杠是什么让我搞砸了...删除它修复它..谢谢!【参考方案3】:os.path.join()
可以与os.path.sep
结合使用以创建绝对路径而不是相对路径。
os.path.join(os.path.sep, 'home','build','test','sandboxes',todaystr,'new_sandbox')
【讨论】:
使用os.path.sep
作为第一个元素来构建绝对路径比这里的任何其他答案都好!使用os.path
而不是基本的str 方法的全部意义在于避免编写/
。将每个子目录作为新参数并删除所有斜杠也很棒。确保检查todaystr
不以斜杠开头可能是个好主意! ;)
这也适用于 Windows(python 2.7.6)。它没有干扰 'C:\' 并加入了子目录。
@snooze92 唯一的问题是它更冗长且可读性差。
确实如此。我猜要权衡取舍;)【参考方案4】:
不要在路径组件的开头使用正斜杠,除非是指根目录:
os.path.join('/home/build/test/sandboxes', todaystr, 'new_sandbox')
另请参阅:http://docs.python.org/library/os.path.html#os.path.join
【讨论】:
这个答案有正确的信息。太棒了。【参考方案5】:为了帮助理解为什么这种令人惊讶的行为并不完全可怕,请考虑一个接受配置文件名作为参数的应用程序:
config_root = "/etc/myapp.conf/"
file_name = os.path.join(config_root, sys.argv[1])
如果应用程序执行时:
$ myapp foo.conf
将使用配置文件/etc/myapp.conf/foo.conf
。
但考虑一下如果调用应用程序会发生什么:
$ myapp /some/path/bar.conf
那么myapp
应该使用/some/path/bar.conf
的配置文件(而不是/etc/myapp.conf/some/path/bar.conf
或类似的)。
这可能不是很好,但我相信这是绝对路径行为的动机。
【讨论】:
谢谢!在阅读您的答案之前,我一直讨厌这种行为!它记录在 docs.python.org/3.5/library/os.path.html#os.path.join 中,但不是它的动机。 此时正是您需要许多人认为可怕的解决方案的时刻。 不同意,这太糟糕了。在这种情况下,您不应该使用幼稚的sys.argv
输入来确定是否添加config_root
。所有os.path.join
都应该关注的是加入文件路径元素。
问题是名字选错了。 os.path.join(p1, p2)
所做的并不是真正加入p1
和p2
,而是将p2
相对于p1
(或者绝对如果p2
是绝对路径)
所以一个特定的用例应该决定一个函数在所有其他用例中的行为方式?没有意义。【参考方案6】:
这是因为您的'/new_sandbox/'
以/
开头,因此被假定为相对于根目录。删除前导/
。
【讨论】:
【参考方案7】:尝试将 split("/")
和 *
组合用于具有现有连接的字符串。
import os
home = '/home/build/test/sandboxes/'
todaystr = '042118'
new = '/new_sandbox/'
os.path.join(*home.split("/"), todaystr, *new.split("/"))
工作原理...
split("/")
将现有路径转换为列表:['', 'home', 'build', 'test', 'sandboxes', '']
列表前面的*
将列表中的每一项拆分为自己的参数
【讨论】:
这会将其变成相对路径而不是绝对路径【参考方案8】:为了让你的函数更便携,可以这样使用它:
os.path.join(os.sep, 'home', 'build', 'test', 'sandboxes', todaystr, 'new_sandbox')
或
os.path.join(os.environ.get("HOME"), 'test', 'sandboxes', todaystr, 'new_sandbox')
【讨论】:
【参考方案9】:只用new_sandbox
试试
os.path.join('/home/build/test/sandboxes/', todaystr, 'new_sandbox')
【讨论】:
【参考方案10】:这样做,没有多余的斜线
root="/home"
os.path.join(root,"build","test","sandboxes",todaystr,"new_sandbox")
【讨论】:
os.sep
而不是开头的/
【参考方案11】:
请注意,如果您使用 os.path.join()
包含已包含点的扩展名,类似的问题可能会困扰您,这会在您使用 os.path.splitext()
时自动发生。在这个例子中:
components = os.path.splitext(filename)
prefix = components[0]
extension = components[1]
return os.path.join("avatars", instance.username, prefix, extension)
即使extension
可能是.jpg
,您最终会得到一个名为“foobar”的文件夹,而不是一个名为“foobar.jpg”的文件。为防止这种情况,您需要单独附加扩展名:
return os.path.join("avatars", instance.username, prefix) + extension
【讨论】:
【参考方案12】:你可以strip
'/'
:
>>> os.path.join('/home/build/test/sandboxes/', todaystr, '/new_sandbox/'.strip('/'))
'/home/build/test/sandboxes/04122019/new_sandbox'
【讨论】:
【参考方案13】:我建议从第二个和后面的字符串中去掉字符串os.path.sep
,防止它们被解释为绝对路径:
first_path_str = '/home/build/test/sandboxes/'
original_other_path_to_append_ls = [todaystr, '/new_sandbox/']
other_path_to_append_ls = [
i_path.strip(os.path.sep) for i_path in original_other_path_to_append_ls
]
output_path = os.path.join(first_path_str, *other_path_to_append_ls)
【讨论】:
【参考方案14】:os.path.join("a", *"/b".split(os.sep))
'a/b'
更完整的版本:
import os
def join (p, f, sep = os.sep):
f = os.path.normpath(f)
if p == "":
return (f);
else:
p = os.path.normpath(p)
return (os.path.join(p, *f.split(os.sep)))
def test (p, f, sep = os.sep):
print("os.path.join(, ) => ".format(p, f, os.path.join(p, f)))
print(" join(, ) => ".format(p, f, join(p, f, sep)))
if __name__ == "__main__":
# /a/b/c for all
test("\\a\\b", "\\c", "\\") # optionally pass in the sep you are using locally
test("/a/b", "/c", "/")
test("/a/b", "c")
test("/a/b/", "c")
test("", "/c")
test("", "c")
【讨论】:
如果 os.sep 实际上是"\"
怎么办?然后你的第一个例子变成os.path.join("a", *"/b".split("\\"))
,它产生"/b"
...我怀疑这是预期的结果。
已更新 - 我想您必须给出提示,因为您在本地使用的路径 sep 独立于您正在运行的操作系统的路径
是的。或者,可以将两个常用选项分开......但是其他一些操作系统可能会提出第三种。【参考方案15】:
问题是您的笔记本电脑可能正在运行 Window。 Window 烦人地使用反斜杠而不是正斜杠'/'。 使您的程序跨平台(linux/windows/etc)。
如果您希望 os.path.join
正确处理它们,则不应在路径中提供任何斜杠(向前或向后)。你应该使用:
os.path.join(os.environ.get("HOME"), 'test', 'sandboxes', todaystr, 'new_sandbox')
或者抛出一些Path(__file__).resolve().parent
(当前文件的父路径)或任何东西,这样你就不会在os.path.join
中使用任何斜线
【讨论】:
以上是关于为啥 os.path.join() 在这种情况下不起作用?的主要内容,如果未能解决你的问题,请参考以下文章