【问题标题】:Converting output from subprocess to csv.reader object将子进程的输出转换为 csv.reader 对象
【发布时间】:2021-08-18 18:16:49
【问题描述】:

有没有办法从 subprocess 获取输出并将其转换为可迭代的 csv.reader 或 csv.DictReader 对象?这是我一直在尝试的代码:

p2 = subprocess.Popen("sort command...", stdout=subprocess.PIPE)
output = p2.communicate()[0]
edits = csv.reader(output, delimiter="\t")

基本上,我正在对一个大的 CSV 文件进行排序,然后我想将它作为 csv.reader 对象放入 Python。

我得到的错误是

错误:迭代器应该返回字符串,而不是 int(您是否以文本模式打开文件?)

有没有办法将此字节流视为 csv.reader 对象,还是我想错了?

【问题讨论】:

  • ...老实说,我很想做类似'sort command' | python pythonscript.py 的事情,然后从 sys.stdin 读取 python 脚本。
  • 这就是我现在的计划,如果我想不通的话。 :)

标签: csv python-3.x subprocess


【解决方案1】:

这是 Python 3 中的一个问题。CSV 模块需要 unicode 输入,而不是字节字符串。除此之外,csv.reader() 需要一个可迭代对象,例如打开的文件或字符串列表。试试这个:

encoding = 'ascii'    # specify the encoding of the CSV data
p2 = subprocess.Popen(['sort', '/tmp/data.csv'], stdout=subprocess.PIPE)
output = p2.communicate()[0].decode(encoding)
edits = csv.reader(output.splitlines(), delimiter=",")
for row in edits:
    print(row)

如果/tmp/data.csv 包含(我使用逗号作为分隔符):

1,2,3,4 9,10,11,12 A B C D 5,6,7,8

那么输出将是:

['1', '2', '3', '4'] ['5', '6', '7', '8'] ['9', '10', '11', '12'] ['A B C D']

【讨论】:

【解决方案2】:

以下内容对我有用(即使文档警告从stdout 阅读)。用io.TextIOWrapper() 包装stdout 支持在字段数据中嵌入换行符。

这样做允许使用生成器,其优点是允许以增量方式读取stdout,一次一行。

p2 = subprocess.Popen(["sort", "tabbed.csv"], stdout=subprocess.PIPE)
output = io.TextIOWrapper(p2.stdout, newline=os.linesep)
edits = csv.reader((line for line in output), delimiter="\t")
for row in edits:
    print(row)

输出:

['1', '2', '3', '4']
['5', '6', '7', '8']
['9', '10', '11', '12']
['a', 'b\r\nx', 'c', 'd']

tabbed.csv 输入测试文件包含以下内容(其中» 表示制表符, 表示换行符):

1»2»3»4
9»10»11»12
a»"b≡x"»c»d
5»6»7»8

【讨论】:

  • 您应该传递一个列表而不是字符串作为 POSIX 系统上的命令("sort tabbed.csv" 将失败)。另外,to enable embedded newlines, you could use io.TextIOWrapper().
  • @J.F.Sebastian:很好的建议。谢谢。
  • 使用newline="" 支持在其他系统上创建的文件(如csv 文档推荐)
  • @J.F.Sebastian:我没有,因为这不是在另一个系统上创建的文件,这样做不支持嵌入式换行符。
  • newline=os.linesepnewline="" 相比没有任何意义。 1.csv 使用\r\n 创建文件,无论系统如何:为什么要使用os.linesep?无论如何,您为什么要主动拒绝来自其他系统的文件? 2. csv "reader is hard-coded to identify either '\r' or '\n' as end-of-line" -- 提供newline=os.linesep(通用换行符)具有误导性模式关闭)在这里。 3. 您应该将newline 参数传递给TextIOWrapper(以避免破坏嵌入的换行符)。注意:newline='' 表示通用换行已打开,但换行未翻译。
【解决方案3】:

要启用文本模式,请传递universal_newlines=True参数:

#!/usr/bin/env python3
import csv
from subprocess import Popen, PIPE

with Popen(["sort", "a.csv"], stdout=PIPE, universal_newlines=True) as p:
    print(list(csv.reader(p.stdout, delimiter="\t")))

如果您需要解释嵌入在引用字段中的换行符,则创建io.TextIOWrapper,以传递newline='' 参数:

#!/usr/bin/env python3
import csv
import io
from subprocess import Popen, PIPE

with Popen(["sort", "a.csv"], stdout=PIPE) as p, \
     io.TextIOWrapper(p.stdout, newline='') as text_file:
    print(list(csv.reader(text_file, delimiter="\t")))

另外,TextIOWrapper 允许显式指定字符编码(否则使用默认的locale.getpreferredencoding(False))。

注意:您不需要外部sort 命令。您可以在纯 Python 中对行进行排序:

#!/usr/bin/env python3
import csv

with open('a.csv', newline='') as text_file:
    rows = list(csv.reader(text_file, delimiter="\t"))
    rows.sort()
    print(rows)

注意:更高版本对 csv 行而不是物理行进行排序(如果需要,您可以对行进行排序)。

【讨论】:

    【解决方案4】:

    如果您的 CSV 文件具有列标题,则此方法有效。

    [ user@system currentDir ]$ ./ProgramThatCreatesCSVData
    first,second,third,fourth
    1,2,3,4
    9,10,11,12
    a,b,c,d
    5,6,7,8
    [ user@system currentDir ]$
    [ user@system currentDir ]$
    [ user@system currentDir ]$
    [ user@system currentDir ]$ cat CSVtoDict.py
    #!/usr/bin/python3
    """Sample program to open a pipe to run a command.
    That command generates a CSV with heading names in the first row.
    Output of this program is a conversion of that CSV to a list of dictionaries,
    in pprint format."""
    
    import csv, pprint, subprocess, io
    
    pipe = subprocess.Popen(["./ProgramThatCreatesCSVData"], stdout=subprocess.PIPE)
    pipeWrapper = io.TextIOWrapper(pipe.stdout)
    pipeReader = csv.DictReader(pipeWrapper)
    listOfDicts = [ dict(row) for row in pipeReader ]
    
    pprint.pprint(listOfDicts)
    
    [ user@system currentDir ]$
    [ user@system currentDir ]$
    [ user@system currentDir ]$
    [ user@system currentDir ]$ python3 CSVtoDict.py
    [{'first': '1', 'fourth': '4', 'second': '2', 'third': '3'},
     {'first': '9', 'fourth': '12', 'second': '10', 'third': '11'},
     {'first': 'a', 'fourth': 'd', 'second': 'b', 'third': 'c'},
     {'first': '5', 'fourth': '8', 'second': '6', 'third': '7'}]
    [ user@system currentDir ]$
    

    【讨论】:

      猜你喜欢
      • 2017-04-24
      • 2017-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多