Python中类似于Perl的(<>)的等效方法是什么?fileinput无法正常工作。

7
在Perl中,我们使用:
while (<>) {
    # process files given as command line arguments
}

在Python中,我发现:

import fileinput
for line in fileinput.input():
    process(line)

但是,如果在命令行中给定的文件不存在怎么办?

例如:python test.py test1.txt test2.txt filenotexist1.txt filenotexist2.txt test3.txt

我尝试过使用try: except: nextfile的各种方式,但似乎无法使其正常工作。

对于上述命令行,脚本应该运行test1-3.txt,但当找不到文件时,只需静默地跳转到下一个文件。

Perl做得非常好。我在网上搜索了很多,但无法找到答案。


“但是当文件不存在时,只是静默地跳到下一个文件?” 真的吗? 为什么? 如果文件不存在,为什么不应该完全中断? - S.Lott
1
@S.Lott:类似grep的Unix命令行工具,它们操作有效的参数,但对不存在的文件只发出警告,并且这些警告不会导致整个命令中止,尽管它确实会使命令以错误状态退出。话虽如此,Perl在不存在的文件上也不是“静默”的,它也会发出警告。 - runrig
1
@S.Lott,有一些非常合理的情况,例如读取一组配置文件并忽略其中一些不存在的情况。 - moinudin
1
@S.Lott:一个不存在的文件是否代表致命错误取决于每个应用程序。有些应用程序应该在不存在的文件上中止;例如,如果守护进程的配置文件不存在,则守护进程可能选择中止,因为需要配置文件。然而,其他应用程序可能不会这样做;例如,缺少配置文件可能只意味着应用程序应该使用默认值。知道如何处理这两种情况是有用的。 - Brian Clapper
7个回答

5
import sys
import os

for f in sys.argv[1:]:
    if os.path.exists(f):
        for line in open(f).readlines():
            process(line)

谢谢这个回答。是否有其他使用最简单的按键的答案?在Perl中,只需要使用“while (<>) { }”几个字符即可。Python中是否有类似的快捷方式? - ihightower
2
@ihightower 是的。将我的方法放入一个模块中并导入它。然后你只需要 for line in read_lines():。Python并不追求使用最少的按键来使用晦涩难懂的运算符,所以你不会像Perl的 <> 那样找到一些简洁的东西,但是如果你非常需要的话,你可以将该方法重命名为类似 rl() 的名称,以获得 for l in rl(): - moinudin
2
这种方法未考虑到其他可能出现的错误,比如文件不可读、文件是目录、文本文件被锁定等。我真的同意原帖中提到的,如果 fileinput 想要有用,它应该提供对其操作的控制。 - tripleee

3

像这样的东西;

import sys

for f in sys.argv[1:]:
    try:
        data = open(f).readlines()
        process(data)
    except IOError:
        continue

3

将@Brian的答案转换为生成器,并捕获IOError而不是测试存在性,这更符合Pythonic规范,如果失败则向stderr打印警告:

import sys

def read_files(files = None):
  if not files:
    files = sys.argv[1:]
  for file in files:
    try:
      for line in open(file):
        yield line
    except IOError, e:
      print >>sys.stderr, 'Warning:', e

for line in read_files():
  print line,

输出(文件baz不存在):

$ python read_lines.py foo bar baz
line 1 of foo
line 2 of foo
line 1 of bar
line 2 of bar
Warning: [Errno 2] No such file or directory: 'baz'

你可能想要稍微花费一些精力来整理一下错误信息,但这可能并不值得。


请注意,捕获IOError将会陷入并忽略不止是“文件不存在”的问题。如果该文件存在但无法读取,您将永远不会知道。当然,这可能是可以接受的,具体取决于应用程序;但是,如果您想区分不存在的文件和读取现有文件时出现的错误,则不能捕获和忽略IOError - Brian Clapper
@Brian 的确如此,但在这种情况下,我认为这比仅显示“文件不存在”要好。 - moinudin
做Perl所做的。不要忽略错误,将它们打印到标准输出(stdout)。 - runrig
@runrig,这不是被要求的内容,但这是一个有效的观点。我会选择stderr。 - moinudin
1
@Brian OP 的目标是在这里模仿 Perl。当 Perl 无法读取文件时,它不会崩溃,我的解决方案也是如此。 - moinudin
显示剩余3条评论

2
您可以使用fileinput模块解决您的问题,具体方法如下:
import fileinput

input = fileinput.input()
while True:
    try:
        process(input.next())
    except IOError:
        input.nextfile()
    except StopIteration:
        break

很遗憾,你不能使用for循环,因为IOException会打断它。


1
在此加1分!except子句应该在nextfile()之前有用地执行一些操作,例如except IOError, e: sys.stderr.write("%s: %s: %s\n" % (sys.argv[0], input.filename(), os.strerror(e.errno))) - tripleee
+1 感谢 JooMing,tripleee - 很有用的解决方案,可以与 fileinput 保持一致(对我现有代码的快速简便更改)。 - azhrei

1

我尝试实现@VGE的建议,但我的尝试并不太优雅。我希望能得到任何改进的建议。

import sys, fileinput, errno, os

class nosuchfile:
    def readlines(foo, bar):
        return []
    def close(arg):
        pass

EXITCODE=0

def skip_on_error (filename, mode):
    """Function to pass in as fileinput.input(openhook=...) hook function.
    Instead of give up on the first error, skip the rest of the file and
    continue with the next file in the input list.

    In case of an error from open() an error message is printed to standard
    error and the global variable EXITCODE gets overwritten by a nonzero
    value.
    """
    global EXITCODE
    try:
        return open(filename, mode)
    except IOError, e:
        sys.stderr.write ("%s: %s: %s\n" % (sys.argv[0], filename, os.strerror(e.errno)))
        EXITCODE = 1
        return nosuchfile()

def main ():
    do_stuff(fileinput.input(openhook=skip_on_error))
    return EXITCODE

占位符虚拟文件句柄类nosuchfile和全局变量EXITCODE都是相当严重的缺陷。我试图弄清楚如何传递对本地作用域退出代码变量的引用,但放弃了。

这也未能处理读取时发生的错误,但大多数错误情况似乎仍然发生在open中。


1
简单,明确,无声:
import fileinput
from os.path import exists
import sys

for line in fileinput.input(files=filter(exists, sys.argv[1:])):
    process(line)

0
也许您可以使用openhook参数来控制不存在的文件。

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