Python 子进程;无法读取 stdout

1 投票
1 回答
2361 浏览
提问于 2025-04-16 07:50

我有大约50万个文本文件,总共大约7个GB的数据。我正在用Python把它们放进一个sqlite数据库里。我创建了两个表,第一个表是主键和文件的超链接。

第二个表我使用了一个同事用Perl开发的实体提取器。

为了实现这个,我使用了subprocess.Popen()。在这之前,我在每次循环时都打开Perl,但这样做太耗资源,根本没法用。

我需要Perl是动态的,能够在它和我的程序之间来回传递数据,并且在我没有告诉它之前,它不能结束。Perl被修改过,现在它可以接受整个文件的内容作为输入,并在接收到换行符时输出结果。但我在读取数据时遇到了麻烦……

如果我使用communicate,下一次循环时我的子进程就会被终止,导致我出现输入输出错误。如果我尝试使用readline()或read(),程序就会卡住。这里有一些我遇到的不同情况的例子。

这会导致我的系统死锁,我需要强制关闭Python才能继续。

numberExtractor = subprocess.Popen(["C:\\Perl\\bin\\perl5.10.0.exe","D:\\MyDataExtractor\\extractSerialNumbers.pl"], stdout=subprocess.PIPE, stdin= subprocess.PIPE)
for infile in glob.glob(self.dirfilename + '\\*\\*.txt'):
   f = open(infile)
   reportString = f.read()
   f.close()

   reportString = reportString.replace('\n',' ')
   reportString = reportString.replace('\r',' ')
   reportString = reportString +'\n'

   numberExtractor.stdin.write(reportString)
   x = numberExtractor.stdout.read()        #I can not see the STDOUT, python freezes and does not run past here.

   print x

这会取消子进程,导致我在下一次循环时出现输入输出错误。

numberExtractor = subprocess.Popen(["C:\\Perl\\bin\\perl5.10.0.exe","D:\\MyDataExtractor\\extractSerialNumbers.pl"], stdout=subprocess.PIPE, stdin= subprocess.PIPE)
for infile in glob.glob(self.dirfilename + '\\*\\*.txt'):

   f = open(infile)
   reportString = f.read()
   f.close()

   reportString = reportString.replace('\n',' ')
   reportString = reportString.replace('\r',' ')
   reportString = reportString +'\n'
   numberExtractor.stdin.write(reportString)
   x = numberExtractor.communicate()   #Works good, I can see my STDOUT from perl but the process terminates and will not run on the next iteration

   print x

如果我就这样运行,它会顺利执行所有代码。打印的内容是每个文件项的', mode 'rb' at 0x015dbf08>。

numberExtractor = subprocess.Popen(["C:\\Perl\\bin\\perl5.10.0.exe","D:\\MyDataExtractor\\extractSerialNumbers.pl"], stdout=subprocess.PIPE, stdin= subprocess.PIPE)
for infile in glob.glob(self.dirfilename + '\\*\\*.txt'):
   f = open(infile)
   reportString = f.read()
   f.close()

   reportString = reportString.replace('\n',' ')
   reportString = reportString.replace('\r',' ')
   reportString = reportString +'\n'

   numberExtractor.stdin.write(reportString)
   x = numberExtractor.stdout                #I can not get the value of the object, but it runs through all my files fine.

   print x

希望我只是犯了个简单的错误,但有没有办法让我只把一个文件发送给Perl(作为输入),获取输出,然后在循环中重复这个过程,而不需要为每个文件重新打开子进程呢?

1 个回答

2

考虑使用命令行工具,这样生活会简单很多。

perl extractSerialNumbers.pl *.txt | python load_database.py

别费劲去让Python去启动perl之类的。直接从perl读取结果,然后在Python中处理这些结果就行了。

因为这两个程序是同时运行的,所以通常速度很快,而且会占用很多CPU资源,但你自己写的代码却不需要太多。

在Python程序(load_database.py)中,你可以简单地使用 fileinput 模块来读取从标准输入传来的整个文件。

import fileinput
for line in fileinput.input():
    load the row into the database

如果让命令行来处理设置管道的繁琐工作,那么在Python程序中你需要做的就差不多这些了。

撰写回答