2009-11-16 42 views
49

Tôi đã viết một tiện ích dòng lệnh sử dụng getopt để phân tích các đối số được đưa ra trên dòng lệnh. Tôi cũng muốn có một tên tập tin là một đối số tùy chọn, chẳng hạn như trong các tiện ích khác như grep, cắt vv Vì vậy, tôi muốn nó phải có việc sử dụng sauĐọc từ Tệp, hoặc STDIN

tool -d character -f integer [filename] 

Làm thế nào tôi có thể thực hiện như sau ?

  • nếu tên tệp được cung cấp, hãy đọc từ tệp.
  • nếu tên tệp không được cung cấp, hãy đọc từ STDIN.
+2

xem thêm http://unix.stackexchange.com/questions/47098/how-do-i-make-python-programs-behave-like-proper-unix-tools/47543#47543 – magnetar

Trả lời

47

Trong thuật ngữ đơn giản:

import sys 
# parse command line 
if file_name_given: 
    inf = open(file_name_given) 
else: 
    inf = sys.stdin 

Tại thời điểm này, bạn sẽ sử dụng inf để đọc từ tập tin. Tùy thuộc vào việc một tên tệp đã được đưa ra, điều này sẽ đọc từ tệp đã cho hay từ stdin.

Khi bạn cần để đóng tập tin, bạn có thể làm điều này:

if inf is not sys.stdin: 
    inf.close() 

Tuy nhiên, trong nhiều trường hợp nó sẽ là vô hại để đóng sys.stdin nếu bạn đang thực hiện với nó.

+0

Liệu raw_input() và input() có đọc từ inf không? – thefourtheye

+0

@thefourtheye: Có, cả hai hàm đó sẽ đọc từ một tệp hoặc từ 'sys.stdin'. –

+2

Tôi tìm thấy một cách khác để giải quyết vấn đề này, tôi viết blog về nó ở đây http://dfourtheye.blogspot.in/2013/05/python-equivalent-of-cs-freopen.html và thêm một câu trả lời cho câu hỏi này là tốt. – thefourtheye

61

Module fileinput có thể làm những gì bạn muốn - giả định các đối số không lựa chọn là ở args thì:

import fileinput 
for line in fileinput.input(args): 
    print line 

Nếu args trống sau đó fileinput.input() sẽ đọc từ stdin; nếu không nó sẽ đọc từ mỗi tập tin lần lượt, theo cách tương tự như của Perl while(<>).

+0

Điều này cũng tốt của một câu trả lời, nhưng không phải là khá phổ biến. Tôi sẽ nhớ sử dụng fileinput lần sau nếu thích hợp. –

+0

Nó hoạt động mà không có 'args' quá. – Gabriel

+0

Phải, nhưng nếu bạn đang sử dụng 'getargs' (như OP là) thì bạn có thể chỉ muốn chuyển qua số dư còn lại thay vì' sys.argv [1:] '(là mặc định). – SimonJ

0

Cái gì như:

if input_from_file: 
    f = open(file_name, "rt") 
else: 
    f = sys.stdin 
inL = f.readline() 
while inL: 
    print inL.rstrip() 
    inL = f.readline() 
8

Để tận dụng tuyên bố python của with, người ta có thể sử dụng đoạn mã sau:

import sys 
with open(sys.argv[1], 'r') if len(sys.argv) > 1 else sys.stdin as f: 
    # read data using f 
    # ...... 
+0

Giải pháp của bạn sẽ đóng 'sys.stdin', do đó các lệnh gọi hàm' input' sau câu lệnh 'with' sẽ tăng' ValueError'. –

5

Tôi thích sử dụng "-" như một chỉ báo rằng bạn nên đọc từ stdin, nó rõ ràng hơn:

import sys 
with open(sys.argv[1], 'r') if sys.argv[1] is not "-" else sys.stdin as f: 
    pass # do something here 
+2

Giải pháp của bạn sẽ đóng 'sys.stdin', do đó các lệnh gọi hàm' input' sau câu lệnh 'with' sẽ tăng' ValueError'. –

+1

@TimofeyBondarev Điều đó có thể đúng .. nhưng thường xuyên nhất đầu vào chỉ được sử dụng một lần trong một tập lệnh. Đây là một cấu trúc hữu ích. – javadba

11

Tôi thích thành ngữ chung của việc sử dụng ngữ cảnh manag er, nhưng các giải pháp tầm thường (quá) kết thúc lên đóng sys.stdin khi bạn ra khỏi tuyên bố with, mà tôi muốn tránh.

Vay từ this answer, đây là một cách giải quyết:

import sys 
import contextlib 

@contextlib.contextmanager 
def _smart_open(filename, mode='Ur'): 
    if filename == '-': 
     if mode is None or mode == '' or 'r' in mode: 
      fh = sys.stdin 
     else: 
      fh = sys.stdout 
    else: 
     fh = open(filename, mode) 
    try: 
     yield fh 
    finally: 
     if filename is not '-': 
      fh.close() 

if __name__ == '__main__': 
    args = sys.argv[1:] 
    if args == []: 
     args = ['-'] 
    for filearg in args: 
     with _smart_open(filearg) as handle: 
      do_stuff(handle) 

Tôi cho rằng bạn có thể đạt được something similar with os.dup() nhưng mã tôi nấu chín lên để làm điều đó hóa ra là phức tạp hơn và kỳ diệu hơn, trong khi ở trên là hơi clunky nhưng rất đơn giản.

+0

Cảm ơn rất nhiều! Đây chính xác là những gì tôi đang tìm kiếm. Rất rõ ràng và thẳng về phía trước giải pháp. – edisonex