วิธีการเปิดไฟล์ในไดเรกทอรีเดียวกันกับสคริปต์ Python อย่างน่าเชื่อถือ


157

ฉันเคยเปิดไฟล์ที่อยู่ในไดเรกทอรีเดียวกันกับสคริปต์ Python ที่กำลังรันอยู่โดยใช้คำสั่งเช่น

open("Some file.txt", "r")

อย่างไรก็ตามฉันค้นพบว่าเมื่อสคริปต์รันใน Windows โดยการดับเบิลคลิกมันจะพยายามเปิดไฟล์จากไดเรกทอรีที่ไม่ถูกต้อง

ตั้งแต่นั้นมาฉันใช้คำสั่งของแบบฟอร์ม

open(os.path.join(sys.path[0], "Some file.txt"), "r")

เมื่อใดก็ตามที่ฉันต้องการเปิดไฟล์ สิ่งนี้ใช้ได้กับการใช้งานเฉพาะของฉัน แต่ฉันไม่แน่ใจว่าsys.path[0]อาจล้มเหลวในกรณีการใช้งานอื่น ๆ

ดังนั้นคำถามของฉันคือวิธีที่ดีที่สุดและน่าเชื่อถือที่สุดในการเปิดไฟล์ที่อยู่ในไดเรกทอรีเดียวกับสคริปต์ Python ที่กำลังทำงานอยู่คืออะไร?

นี่คือสิ่งที่ฉันสามารถเข้าใจได้:

  • os.getcwd()และos.path.abspath('')ส่งคืน "ไดเรกทอรีทำงานปัจจุบัน" ไม่ใช่ไดเรกทอรีสคริปต์

  • os.path.dirname(sys.argv[0])และos.path.dirname(__file__)คืนค่าพา ธ ที่ใช้เรียกสคริปต์ซึ่งอาจสัมพันธ์หรือแม้แต่ว่างเปล่า (หากสคริปต์อยู่ใน cwd) นอกจากนี้__file__จะไม่มีอยู่เมื่อรันสคริปต์ใน IDLE หรือ PythonWin

  • sys.path[0]และos.path.abspath(os.path.dirname(sys.argv[0]))ดูเหมือนจะส่งคืนไดเรกทอรีสคริปต์ ฉันไม่แน่ใจว่ามีความแตกต่างระหว่างสองสิ่งนี้หรือไม่

แก้ไข:

ฉันเพิ่งรู้ว่าสิ่งที่ฉันต้องการจะอธิบายได้ดีกว่าว่า "เปิดไฟล์ในไดเรกทอรีเดียวกันกับโมดูลที่มี" กล่าวอีกนัยหนึ่งถ้าฉันนำเข้าโมดูลที่ฉันเขียนนั้นอยู่ในไดเรกทอรีอื่นและโมดูลนั้นเปิดไฟล์ฉันต้องการให้ค้นหาไฟล์ในไดเรกทอรีของโมดูล ฉันไม่คิดว่าจะพบสิ่งใดที่ฉันสามารถทำได้ ...

คำตอบ:


199

ฉันมักจะใช้:

__location__ = os.path.realpath(
    os.path.join(os.getcwd(), os.path.dirname(__file__)))

การjoin()โทรเตรียมไดเรกทอรีการทำงานปัจจุบัน แต่เอกสารระบุว่าหากเส้นทางบางเส้นทางสมบูรณ์เส้นทางอื่นทั้งหมดที่เหลืออยู่จะถูกทิ้ง ดังนั้นgetcwd()จะลดลงเมื่อdirname(__file__)ส่งกลับเส้นทางที่แน่นอน

นอกจากนี้การrealpathโทรจะแก้ไขลิงก์สัญลักษณ์หากพบ วิธีนี้จะช่วยหลีกเลี่ยงปัญหาเมื่อปรับใช้กับ setuptools บนระบบ Linux (สคริปต์จะเชื่อมโยงกับ/usr/bin/- อย่างน้อยบน Debian)

คุณสามารถใช้สิ่งต่อไปนี้เพื่อเปิดไฟล์ในโฟลเดอร์เดียวกัน:

f = open(os.path.join(__location__, 'bundled-resource.jpg'));
# ...

ฉันใช้สิ่งนี้เพื่อรวมทรัพยากรกับแอปพลิเคชั่น Django หลายตัวทั้ง Windows และ Linux และมันทำงานได้อย่างน่าหลงใหล!


4
หาก__file__ไม่สามารถนำมาใช้แล้วใช้แทนsys.argv[0] dirname(__file__)ส่วนที่เหลือควรทำงานตามที่คาดไว้ ฉันชอบใช้__file__เพราะในรหัสห้องสมุดsys.argv[0]อาจไม่ชี้ไปที่รหัสของคุณเลยโดยเฉพาะถ้านำเข้าผ่านสคริปต์ของบุคคลที่สาม
André Caron

1
ปัญหาเกี่ยวกับสิ่งนี้คือมันจะแตกต่างกันถ้าไฟล์ที่คุณกำลังเรียกใช้นั้นมาจากตัวขัดขวางโดยตรงหรือถ้ามันถูกอิมพอร์ต ดูคำตอบของฉันสำหรับความแตกต่างระหว่างไฟล์และ sys.argv [0]
Zimm3r

ดังนั้นถูกต้องหรือไม่ที่จะบอกว่ารูปแบบที่อธิบายไว้ในคำตอบของ Zimm3r นั้นได้รับการแก้ไขโดยใช้realpath( join( getcwd(), dirname(__file__) ))ตามที่อธิบายไว้ที่นี่?
pianoJames

44

อ้างจากเอกสาร Python:

ตามที่เริ่มต้นเมื่อโปรแกรมเริ่มต้นรายการแรกของรายการนี้พา ธ [0] คือไดเรกทอรีที่มีสคริปต์ที่ใช้เพื่อเรียกใช้ตัวแปล Python หากไดเรกทอรีสคริปต์ไม่พร้อมใช้งาน (เช่นถ้าล่ามถูกเรียกใช้แบบโต้ตอบหรือถ้าสคริปต์ถูกอ่านจากอินพุตมาตรฐาน) path [0] เป็นสตริงว่างซึ่งนำ Python ไปยังโมดูลการค้นหาในไดเรกทอรีปัจจุบันก่อน ขอให้สังเกตว่าไดเรกทอรีสคริปต์จะถูกแทรกก่อนที่รายการแทรกเป็นผลมาจาก PYTHONPATH

sys.path [0] คือสิ่งที่คุณกำลังมองหา


10
os.path.join(sys.path[0], 'some file.txt')และสำหรับเส้นทางที่เต็มรูปแบบของไฟล์: ที่ควรจัดการช่องว่างและเครื่องหมายทับอย่างถูกต้องในทุกระบบ
Jacktose

คำตอบนี้เป็นคำถามแรกไม่ใช่คำถามหลังจาก EDIT
mcoolive

22

ตกลงนี่คือสิ่งที่ฉันทำ

sys.argv เป็นสิ่งที่คุณพิมพ์ลงในเทอร์มินัลเสมอหรือใช้เป็นพา ธ ไฟล์เมื่อดำเนินการด้วย python.exe หรือ pythonw.exe

ตัวอย่างเช่นคุณสามารถเรียกใช้ไฟล์ text.py ได้หลายวิธีโดยแต่ละแบบจะให้คำตอบที่แตกต่างกันพวกเขาจะให้เส้นทางที่พิมพ์ด้วยหลามเสมอ

    C:\Documents and Settings\Admin>python test.py
    sys.argv[0]: test.py
    C:\Documents and Settings\Admin>python "C:\Documents and Settings\Admin\test.py"
    sys.argv[0]: C:\Documents and Settings\Admin\test.py

ตกลงรู้ว่าคุณจะได้รับชื่อไฟล์เรื่องใหญ่ตอนนี้เพื่อรับไดเรกทอรีแอปพลิเคชันที่คุณสามารถรู้การใช้ os.path โดยเฉพาะ abspath และ dirname

    import sys, os
    print os.path.dirname(os.path.abspath(sys.argv[0]))

ที่จะส่งออกนี้:

   C:\Documents and Settings\Admin\

มันจะเอาท์พุทนี้เสมอไม่ว่าคุณจะพิมพ์ python test.py หรือ python "C: \ Documents and Settings \ Admin \ test.py"

ปัญหาในการใช้ __file__ ให้ พิจารณาทั้งสองไฟล์ test.py

import sys
import os

def paths():
        print "__file__: %s" % __file__
        print "sys.argv: %s" % sys.argv[0]

        a_f = os.path.abspath(__file__)
        a_s = os.path.abspath(sys.argv[0])

        print "abs __file__: %s" % a_f
        print "abs sys.argv: %s" % a_s

if __name__ == "__main__":
    paths()

import_test.py

import test
import sys

test.paths()

print "--------"
print __file__
print sys.argv[0]

ผลลัพธ์ของ "python test.py"

C:\Documents and Settings\Admin>python test.py
__file__: test.py
sys.argv: test.py
abs __file__: C:\Documents and Settings\Admin\test.py
abs sys.argv: C:\Documents and Settings\Admin\test.py

ผลลัพธ์ของ "python test_import.py"

C:\Documents and Settings\Admin>python test_import.py
__file__: C:\Documents and Settings\Admin\test.pyc
sys.argv: test_import.py
abs __file__: C:\Documents and Settings\Admin\test.pyc
abs sys.argv: C:\Documents and Settings\Admin\test_import.py
--------
test_import.py
test_import.py

ดังนั้นอย่างที่คุณเห็นไฟล์ให้คุณเป็นไฟล์ไพ ธ อนซึ่งมันถูกเรียกใช้จากที่ใดที่ sys.argv [0] ให้ไฟล์ที่คุณรันจากล่ามเสมอ ขึ้นอยู่กับความต้องการของคุณคุณจะต้องเลือกสิ่งที่เหมาะสมกับความต้องการของคุณมากที่สุด


3
นี่เป็นหลักฐานที่แสดงให้เห็นอย่างชัดเจนว่าการใช้งานนั้นสะท้อนถึงเอกสารประกอบ __file__จะควรจะ "มักจะให้คุณเส้นทางไปยังแฟ้มปัจจุบัน" และsys.argv[0]เป็นที่ควรจะ "มักจะให้เส้นทางของสคริปต์ที่ริเริ่มกระบวนการ" ไม่ว่าในกรณีใดการใช้__file__สคริปต์ที่ถูกเรียกใช้จะให้ผลลัพธ์ที่แม่นยำเสมอ
André Caron

หากคุณมีการอ้างอิงถึง__file__ที่ระดับบนสุดของสคริปต์สคริปต์จะทำงานตามที่คาดไว้
Matthew Schinckel

-1

ฉันสามารถใช้รหัสที่จัดทำโดย dcolish ได้สำเร็จเนื่องจากฉันมีปัญหาคล้ายกันกับการอ่านไฟล์ข้อความเฉพาะ ไฟล์ไม่ได้อยู่ใน cwd เช่นเดียวกับไฟล์ Python


1
โปรดอย่าเพิ่ม "ขอบคุณ" เป็นคำตอบ เมื่อคุณมีชื่อเสียงเพียงพอคุณจะสามารถลงคะแนนคำถามและคำตอบที่คุณเห็นว่ามีประโยชน์ - จากการรีวิว
Roberto Caboni

-3

ฉันจะทำแบบนี้:

from os.path import abspath, exists

f_path = abspath("fooabar.txt")

if exists(f_path):
    with open(f_path) as f:
        print f.read()

รหัสข้างต้นสร้างเส้นทางที่แน่นอนไปยังไฟล์โดยใช้abspathและเทียบเท่ากับการใช้normpath(join(os.getcwd(), path))[นั่นมาจาก pydocs] จากนั้นตรวจสอบว่าไฟล์นั้นมีอยู่จริงหรือไม่แล้วใช้ตัวจัดการบริบทเพื่อเปิดเพื่อให้คุณไม่ต้องจำว่าต้องโทรปิดที่จุดจับไฟล์ IMHO การทำเช่นนี้จะช่วยให้คุณเจ็บปวดได้มากในระยะยาว


นี่ไม่ได้ตอบคำถามของผู้โพสต์ dln385 กล่าวโดยเฉพาะว่าos.path.abspathไม่สามารถแก้ไขพา ธ ไปยังไฟล์ในโฟลเดอร์เดียวกันกับสคริปต์หากสคริปต์ไม่ได้อยู่ในไดเรกทอรีปัจจุบัน
André Caron

อา! ฉันคิดว่าผู้ใช้กำลังเรียกใช้สคริปต์นี้ใน dir เดียวกับไฟล์ที่พวกเขาต้องการอ่านไม่ใช่ในโมดูล dir ของบางสิ่งใน PYTHONPATH ของพวกเขา ที่จะสอนให้ฉันเพื่อให้สมมติฐาน ...
dcolish

abspath จะไม่ทำงานเนื่องจากเป็นไปไม่ได้ที่ python runtime จะค้นหาในระบบไฟล์ OS โดยใช้ฟังก์ชั่นเช่นนี้
akshat thakar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.