使用绝对路径打开二进制文件,二进制文件本身无法读取所在目录下的文件

编译打包和其他
回复
whtrys
帖子: 16
注册时间: 2022-03-12 19:09
系统: Ubuntu 22.04.3

使用绝对路径打开二进制文件,二进制文件本身无法读取所在目录下的文件

#1

帖子 whtrys » 2022-04-09 14:57

我写了一个python程序,用pyinstaller打包成了二进制文件。
当我将终端cd到项目目录后,运行./PyCompress 一切正常,但当我在将终端cd到桌面后,用绝对路径/home/whtrys/桌面/PyCompress/PyCompress尝试运行它时,他报错了,报

代码: 全选

FileNotFoundError: [Errno 2] No such file or directory: 'setting.json'
他就无法使用项目目录下的文件
(项目目录如下)
PyCompress
├── i18n
│   ├── en.json
│   └── zh_CN.json
├── img
│   └── bg.jpg
├── PyCompress <---这是二进制文件
└── setting.json <----这个就是报错无法使用的文件

请问这是什么问题。。。谢谢



贴一下打包的过程
目录如下
PyCompress
├── gui.py
├── i18n
│   ├── en.json
│   └── zh_CN.json
├── img
│   └── bg.jpg
├── setting.json
└── zip_cw.py


项目使用GNU v3协议
gui.py:

代码: 全选

# coding=utf-8
"""
Project:PyCompress
File:gui.py
Author:whtry陈
Time:2021-03-27 09:48
程序的GUI文件,方便适配
"""
import json
import os
import sys
import tkinter as tk
import tkinter.filedialog as tkfd
import tkinter.messagebox as tkms

from PIL import Image, ImageTk

import zip_cw

with open('setting.json', 'r', encoding='utf-8') as f:
    setting = json.load(f)
    f.close()

b_compress_item = setting["auto_save_item"]
support_file = [('zip文件', '*.zip')]
support_file_pure = ['zip', 'rar']
compress_item = ''
compress_files = ''
file_path_name = ''

with open(setting["language"], 'r', encoding='utf-8') as f:
    language = json.load(f)
    f.close()


def about_us():
    """
    关于我们
    :return: 无
    """
    # 主界面
    au = tk.Toplevel()
    au.geometry('480x270')
    au.resizable(None, None)
    au.title(language['关于我们'])
    tk.Label(au, text=language["关于我们_全"], font=('微软雅黑', 13)).pack()


def restart_program():
    """
    重启程序
    :return: 无
    """
    python = sys.executable
    os.execl(python, python, *sys.argv)


def decided(bt, filepath):
    """
    打开并判断打开的文件类型
    :param bt: 保存按键
    :param filepath: 文件路径
    :return:
    """
    global compress_files
    global file_path_name
    global compress_item

    # 重启程序以清空缓存的文件
    if compress_item != '':
        restart_program()

    file_list.delete(0, tk.END)
    if filepath == "None":
        filepath = tkfd.askopenfilename(filetypes=support_file)

    file_name_sp = filepath.split('\\')
    file_name_sp = file_name_sp[-1]
    choice = file_name_sp.split('.')
    file_path_name = choice[0]
    choice = choice[-1]
    compress_item = choice

    # TODO : 还有一堆别的格式要接入。。。啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊!
    if choice == '':
        pass
    elif choice == 'zip':
        bt['state'] = 'normal'
        compress_files = zip_cw.get_zip(filepath, file_list)


def save_compress():
    """
    保存解压后的压缩文件
    """
    if compress_item == 'zip':
        zip_cw.save_zip(save_compress_bt, compress_files, file_path_name)
    tkms.showinfo(language['提示'], "已解压")


def become_compress(file=None):
    """
    压缩文件夹
    """

    if b_compress_item == 'zip':
        zip_cw.become_compress(file)


# ——————————————————————GUI——————————————————————
home = tk.Tk()
home.geometry('800x600')
home.resizable(None, None)
home.title(language["主页标题"])
# 页头
# 图
# 创建一个图片管理类
bg_path = './img/bg.jpg'
bg = ImageTk.PhotoImage(image=Image.open(bg_path))
# 按钮
tk.Label(home, height=83, image=bg).pack(side='top', fill='x')  # 背景图

save_compress_bt = tk.Button(home, text=language["选择解压\n保存\n的位置"], font=('微软雅黑', 8),
                             height=4, width=10, command=save_compress, state='disabled')
save_compress_bt.place(x=182, y=6)

tk.Button(home, text=language["打开\n压缩包"], font=('微软雅黑', 8), height=4,
          width=10, command=lambda: decided(save_compress_bt, "None")).place(x=2, y=6)
tk.Button(home, text=language["压缩\n文件夹"], font=('微软雅黑', 8),
          height=4, width=10, command=become_compress).place(x=92, y=6)

tk.Button(home, text=language["关于我们"], font=('微软雅黑', 8),
          height=4, width=10, command=about_us).place(x=717, y=6)

# 中间
tk.Label(home, text=language['文件:']).pack(anchor='w', pady=2)
file_list = tk.Listbox(home, font=("微软雅黑", 11))
file_list.pack(fill='both', expand=1, anchor='center')

# 页脚 tip-小贴士
tk.Label(home, text=language["底部提示"], font=('微软雅黑', 10),
         bg='white').pack(side='bottom', fill='x')

tk.mainloop()
zip_cw.py:

代码: 全选

# coding=utf-8
"""
Project:PyCompress
File:zip_cw.py
Author:whtry陈
Time:2021-03-27 09:47
解压zip
"""
import os
import tkinter.filedialog as tkfd
import tkinter.messagebox as tkms
import zipfile


def recode(dir_names):
    """
    将乱码进行还原
    :param dir_names:解压保存的目录
    """
    os.chdir(dir_names)

    for temp_name in os.listdir('.'):
        try:
            new_name = temp_name.encode('cp437')
            new_name = new_name.decode("gbk")
            os.rename(temp_name, new_name)
            # 传回重新编码的文件名给原文件名
            temp_name = new_name

            if os.path.isdir(temp_name):
                # 对子文件夹进行递归调用
                recode(temp_name)
                # 记得返回上级目录
                os.chdir('..')

        except:
            pass


def get_zip(filename, file_list):
    """
    获取zip压缩包
    :param filename: 打开的压缩包路径
    :param file_list: list窗口
    :return: zip库的zipfile
    """
    print("zip类型")
    zip_files = zipfile.ZipFile(filename, 'r')
    for i in zip_files.namelist():
        file_list.insert(0, i.encode('cp437').decode("gbk"))

    return zip_files


def save_zip(save_zip_bt, zip_files, just_file_name):
    """
    保存压缩包
    :param save_zip_bt:保存按键
    :param zip_files: list窗口
    :param just_file_name: 单纯的文件夹名
    :return:
    """
    if zip_files == '':
        save_zip_bt['state'] = 'disabled'
        tkms.showerror('错误', '请选择要解压的压缩包')
    else:
        save_dir = tkfd.askdirectory()
        if save_dir == '':
            pass
        else:
            just_file_name = just_file_name.split("/")[-1]
            save_dir = '{}/{}'.format(save_dir, just_file_name)
            # 判断是否存在同名文件夹
            if os.path.exists(save_dir):
                tkms.showerror("错误", "存在与该压缩包重名的文件夹")
                return
            else:
                os.mkdir(save_dir)

            for file in zip_files.namelist():
                zip_files.extract(file, save_dir)
            recode(save_dir)


def become_compress(become_compress_dir):
    """
    将文件夹压缩
    :return: 无
    """
    if become_compress_dir is None:
        become_compress_dir = tkfd.askdirectory(title='选择要压缩的文件夹')
    if become_compress_dir == '':
        tkms.showerror("错误", "请选择保存的路径")
    else:
        def walk(path):
            lst = []
            if not os.path.exists(path):
                return -1
            for root, dirs, names in os.walk(path):
                for filename in names:
                    lst.append(os.path.join(root, filename))
            return lst

        lst2 = []
        for i in walk(become_compress_dir):
            lst2.append(i.replace('\\', '/'))

        save_dir = tkfd.askdirectory(title='选择保存的目录')
        if save_dir == '':
            tkms.showerror("错误", "请选择保存的路径")
        else:
            try:
                print(save_dir)
                zip_name = become_compress_dir.split('/')[-1] + '.zip'
                print("不存在相同文件名的文件" + zip_name)
                z = zipfile.ZipFile(save_dir + '/' + zip_name, 'w', zipfile.ZIP_DEFLATED)
                for i in lst2:
                    z.write(filename=i)
                z.close()
                tkms.showinfo("提示", "压缩完成")
            except:
                tkms.showerror("zip压缩模块错误", "错位原因未知")
cd到此目录,使用

代码: 全选

pyinstaller -w -F gui.py --hidden-import PIL._tkinter_finder
进行打包,打包后将dist中打包好的二进制文件剪切到主目录下,删除多余文件,最终形成一开始的
PyCompress
├── i18n
│   ├── en.json
│   └── zh_CN.json
├── img
│   └── bg.jpg
├── PyCompress
└── setting.json
头像
懒蜗牛Gentoo
论坛版主
帖子: 7354
注册时间: 2007-03-02 17:36
系统: Linux Mint

Re: 使用绝对路径打开二进制文件,二进制文件本身无法读取所在目录下的文件

#2

帖子 懒蜗牛Gentoo » 2022-04-09 17:55

虽然世上没有完美的东西,但这并不影响我们追求完美,因为只有偏执狂才TMD能成功。
10.04新手入门——笨兔兔讲述自己的故事
whtrys
帖子: 16
注册时间: 2022-03-12 19:09
系统: Ubuntu 22.04.3

Re: 使用绝对路径打开二进制文件,二进制文件本身无法读取所在目录下的文件

#3

帖子 whtrys » 2022-04-09 20:53

懒蜗牛Gentoo 写了: 2022-04-09 17:55 你可能需要看看这个
https://www.cnblogs.com/wu-wu/p/11077016.html
您的意思是说我必须使用绝对路径而非相对路径吗
头像
astolia
论坛版主
帖子: 6477
注册时间: 2008-09-18 13:11

Re: 使用绝对路径打开二进制文件,二进制文件本身无法读取所在目录下的文件

#4

帖子 astolia » 2022-04-10 10:58

你程序的问题,实际上跟用相对路径绝对路径运行没有关系

发生问题的原因是,代码中使用了相对路径,运行时必须要保证当前目录是程序所在目录
whtrys 写了: 2022-04-09 14:57

代码: 全选

bg_path = './img/bg.jpg'
...
with open('setting.json', 'r', encoding='utf-8') as f:
所以只有你用cd将当前目录改到项目目录后才能正常运行

要么你把相对路径改为和程序所在目录拼接后的绝对路径

代码: 全选

basedir=os.path.dirname(sys.argv[0])
bg_path=os.path.join(basedir, "./img/bg.jpg")
...
with open(os.path.join(basedir, 'setting.json'), 'r', encoding='utf-8') as f:
要么你把当前目录改成程序所在目录

代码: 全选

os.chdir(os.path.dirname(sys.argv[0]))
bg_path = './img/bg.jpg'
...
os.chdir(os.path.dirname(sys.argv[0]))
with open('setting.json', 'r', encoding='utf-8') as f:


还有种方法是用pyinstaller打包时把相关文件一起打包进来,这样只用一个单一的可执行文件即可,不需要再考虑外部文件的路径了

代码: 全选

if hasattr(sys, "_MEIPASS"):
    # 打包后解压路径
    basedir=sys._MEIPASS
else:
    basedir=os.path.dirname(sys.argv[0])
  
bg_path=os.path.join(basedir, "./img/bg.jpg")
...
with open(os.path.join(basedir, 'setting.json'), 'r', encoding='utf-8') as f:
像这样打包

代码: 全选

pyinstaller -w -F gui.py --hidden-import PIL._tkinter_finder --add-data img:img --add-data setting.json:.
就把img目录和setting.json文件一起打包进去了
whtrys
帖子: 16
注册时间: 2022-03-12 19:09
系统: Ubuntu 22.04.3

Re: 使用绝对路径打开二进制文件,二进制文件本身无法读取所在目录下的文件

#5

帖子 whtrys » 2022-04-10 12:02

astolia 写了: 2022-04-10 10:58 你程序的问题,实际上跟用相对路径绝对路径运行没有关系

发生问题的原因是,代码中使用了相对路径,运行时必须要保证当前目录是程序所在目录
whtrys 写了: 2022-04-09 14:57

代码: 全选

bg_path = './img/bg.jpg'
...
with open('setting.json', 'r', encoding='utf-8') as f:
所以只有你用cd将当前目录改到项目目录后才能正常运行

要么你把相对路径改为和程序所在目录拼接后的绝对路径

代码: 全选

basedir=os.path.dirname(sys.argv[0])
bg_path=os.path.join(basedir, "./img/bg.jpg")
...
with open(os.path.join(basedir, 'setting.json'), 'r', encoding='utf-8') as f:
要么你把当前目录改成程序所在目录

代码: 全选

os.chdir(os.path.dirname(sys.argv[0]))
bg_path = './img/bg.jpg'
...
os.chdir(os.path.dirname(sys.argv[0]))
with open('setting.json', 'r', encoding='utf-8') as f:


还有种方法是用pyinstaller打包时把相关文件一起打包进来,这样只用一个单一的可执行文件即可,不需要再考虑外部文件的路径了

代码: 全选

if hasattr(sys, "_MEIPASS"):
    # 打包后解压路径
    basedir=sys._MEIPASS
else:
    basedir=os.path.dirname(sys.argv[0])
  
bg_path=os.path.join(basedir, "./img/bg.jpg")
...
with open(os.path.join(basedir, 'setting.json'), 'r', encoding='utf-8') as f:
像这样打包

代码: 全选

pyinstaller -w -F gui.py --hidden-import PIL._tkinter_finder --add-data img:img --add-data setting.json:.
就把img目录和setting.json文件一起打包进去了
谢谢
whtrys
帖子: 16
注册时间: 2022-03-12 19:09
系统: Ubuntu 22.04.3

Re: 使用绝对路径打开二进制文件,二进制文件本身无法读取所在目录下的文件

#6

帖子 whtrys » 2022-05-05 17:34

astolia 写了: 2022-04-10 10:58 你程序的问题,实际上跟用相对路径绝对路径运行没有关系

发生问题的原因是,代码中使用了相对路径,运行时必须要保证当前目录是程序所在目录
whtrys 写了: 2022-04-09 14:57

代码: 全选

bg_path = './img/bg.jpg'
...
with open('setting.json', 'r', encoding='utf-8') as f:
所以只有你用cd将当前目录改到项目目录后才能正常运行

要么你把相对路径改为和程序所在目录拼接后的绝对路径

代码: 全选

basedir=os.path.dirname(sys.argv[0])
bg_path=os.path.join(basedir, "./img/bg.jpg")
...
with open(os.path.join(basedir, 'setting.json'), 'r', encoding='utf-8') as f:
要么你把当前目录改成程序所在目录

代码: 全选

os.chdir(os.path.dirname(sys.argv[0]))
bg_path = './img/bg.jpg'
...
os.chdir(os.path.dirname(sys.argv[0]))
with open('setting.json', 'r', encoding='utf-8') as f:


还有种方法是用pyinstaller打包时把相关文件一起打包进来,这样只用一个单一的可执行文件即可,不需要再考虑外部文件的路径了

代码: 全选

if hasattr(sys, "_MEIPASS"):
    # 打包后解压路径
    basedir=sys._MEIPASS
else:
    basedir=os.path.dirname(sys.argv[0])
  
bg_path=os.path.join(basedir, "./img/bg.jpg")
...
with open(os.path.join(basedir, 'setting.json'), 'r', encoding='utf-8') as f:
像这样打包

代码: 全选

pyinstaller -w -F gui.py --hidden-import PIL._tkinter_finder --add-data img:img --add-data setting.json:.
就把img目录和setting.json文件一起打包进去了
请问假如我用os.getcwd()会有什么区别吗
头像
astolia
论坛版主
帖子: 6477
注册时间: 2008-09-18 13:11

Re: 使用绝对路径打开二进制文件,二进制文件本身无法读取所在目录下的文件

#7

帖子 astolia » 2022-05-10 12:43

whtrys 写了: 2022-05-05 17:34 请问假如我用os.getcwd()会有什么区别吗
你会问出这个问题就表示你并没有弄懂相对路径到底是什么。当路径不是以/开头时,都视为相对于程序的工作目录(working directory),工作目录也称为当前目录(current directory)。

举个实际的例子
whtrys 写了: 2022-04-09 14:57 PyCompress
├── i18n
│ ├── en.json
│ └── zh_CN.json
├── img
│ └── bg.jpg
├── PyCompress
└── setting.json
假设PyCompress目录是放在/tmp下面。
1. 当你在shell里执行cd /tmp/PyCompress后,shell的工作目录就变成了/tmp/PyCompress,再运行./PyCompress,由于默认会继承父进程的运行环境,所以PyCompress程序的工作目录也是/tmp/PyCompress,当执行到open('setting.json', 'r', encoding='utf-8')一句时,会去打开/tmp/PyCompress/setting.json文件。这是用相对路径运行打开文件成功
2. 如果在shell里执行了cd /tmp,再运行./PyCompress/PyCompress,程序的工作目录是/tmp,所以会去打开/tmp/setting.json文件。这是用相对路径运行打开文件失败
3. 如果在shell里执行了cd /tmp/PyCompress,再运行/tmp/PyCompress/PyCompress,程序的工作目录是/tmp/PyCompress,所以去打开/tmp/PyCompress/setting.json文件。这是用绝对路径运行打开文件成功
4. 如果在shell里执行了cd /tmp,再运行/tmp/PyCompress/PyCompress,程序的工作目录是/tmp,所以去打开/tmp/setting.json文件。这是用绝对路径运行打开文件失败

所以我上面会说
astolia 写了: 2022-04-10 10:58 你程序的问题,实际上跟用相对路径绝对路径运行没有关系
因为真正有关系的,是你程序运行时的工作目录/当前目录与所要读取文件的相对路径之间的相对位置。

回到你的问题,os.getcwd()是获取当前运行环境的工作目录,open('setting.json')和open(os.getcwd() + '/setting.json')没有区别。
回复