在Python3中如何使用StringIO?

640

我使用的是Python 3.2.1,但无法导入StringIO模块。我用io.StringIO代替它,虽然可以正常工作,但无法像这样与numpygenfromtxt一起使用:

x="1 3\n 4.5 8"        
numpy.genfromtxt(io.StringIO(x))

I get the following error:

TypeError: Can't convert 'bytes' object to str implicitly  

当我写import StringIO时,它会显示:

ImportError: No module named 'StringIO'
9个回答

1066

当我写 import StringIO 时,它会提示没有这个模块。

来自 Python 3.0 的新特性:

StringIOcStringIO 模块已经被移除。现在,你需要导入 io 模块,并使用 io.StringIO 或者 io.BytesIO 分别处理文本和数据。

.


一个可能有用的方法,可以修复一些 Python 2 代码,使其也能在 Python 3 中运行(买家需谨慎):

try:
    from StringIO import StringIO ## for Python 2
except ImportError:
    from io import StringIO ## for Python 3

注意:此示例可能与问题的主要问题无关,仅作为在泛泛地解决缺少 StringIO 模块时需要考虑的内容。对于更直接的解决方案,有关消息 TypeError: Can't convert 'bytes' object to str implicitly,请参见此答案


14
值得一提的是,这两者不同,如果你仅仅进行这个修改,就可能会出现“TypeError”(期望字符串参数,得到了“字节”)的错误。在Python 3中,你需要仔细区分“bytes”和“str”(Unicode)。 - Andy Hayden
8
对于像我这样的新手:from io import StringIO 的意思是你需要将其调用为StringIO(),而不是io.StringIO()。 - Noumenon
13
如何实现与Python 2和3的兼容性:只需使用 from io import StringIO - Oleh Prypin
9
在Python 3中,对于numpy.genfromtxt()来说,“THIS IS SIMPLY WRONG”。请参考Roman Shapovalov的答案。 - Bill Huang
3
@nobar: 选择后者。原问题使用的是Python 3.x,其中模块StringIO已经被删除,应该改为导入from io import BytesIO。我在Python 3.5环境下进行了测试,使用了eclipse pyDev和win7 x64。如果我有错误,请告诉我。谢谢。 - Bill Huang
显示剩余11条评论

172

在我的情况下,我使用了:

from io import StringIO

79

在 Python 3 中,numpy.genfromtxt 需要一个字节流作为参数。使用以下代码:

numpy.genfromtxt(io.BytesIO(x.encode()))

27

Roman Shapovalov的代码应该可以在Python 3.x以及Python 2.6 / 2.7中正常工作。以下是完整示例的代码:

import io
import numpy
x = "1 3\n 4.5 8"
numpy.genfromtxt(io.BytesIO(x.encode()))

输出:

array([[ 1. ,  3. ],
       [ 4.5,  8. ]])

Python 3.x的解释:

  • numpy.genfromtxt接受字节流(一个文件对象,被解释为字节而不是Unicode)。
  • io.BytesIO接受一个字节字符串并返回一个字节流。另一方面,io.StringIO会接受一个Unicode字符串并返回一个Unicode流。
  • x被分配了一个字符串文字,在Python 3.x中是一个Unicode字符串。
  • encode()将Unicode字符串x转换为字节字符串,从而给io.BytesIO提供有效参数。

对于Python 2.6/2.7唯一的区别是x是一个字节字符串(假设没有使用from __future__ import unicode_literals),然后encode()仍然将字节字符串x转换为相同的字节字符串。所以结果是相同的。


由于这是SO关于StringIO最受欢迎的问题之一,这里对导入语句和不同Python版本进行了更多解释。

以下是将字符串转换为流的类:

  • io.BytesIO (Python 2.6、2.7和3.x) - 接受字节串并返回字节流。
  • io.StringIO (Python 2.6、2.7和3.x) - 接受Unicode字符串并返回Unicode流。
  • StringIO.StringIO (Python 2.x) - 接受字节串或Unicode字符串。如果是字节串,则返回字节流;如果是Unicode字符串,则返回Unicode流。
  • cStringIO.StringIO (Python 2.x) - StringIO.StringIO的更快版本,但不能接受包含非ASCII字符的Unicode字符串。
注意,StringIO.StringIO被导入为from StringIO import StringIO,然后被用作StringIO(...)。要么这样做,要么你可以import StringIO,然后使用StringIO.StringIO(...)。模块名和类名恰好相同。这与datetime类似。
根据你支持的Python版本选择使用哪个:
  • 如果你只支持Python 3.x: 只需使用io.BytesIOio.StringIO,具体取决于您处理的数据类型。

  • 如果您同时支持Python 2.6/2.7和3.x,或者正试图将代码从2.6/2.7转换为3.x: 最简单的选择仍然是使用io.BytesIOio.StringIO。虽然StringIO.StringIO很灵活,因此在2.6/2.7中似乎更受欢迎,但这种灵活性可能掩盖了在3.x中会出现的错误。例如,我有一些代码根据Python版本使用StringIO.StringIOio.StringIO,但实际上传递了一个字节字符串,因此当我在Python 3.x中测试它时失败了,必须进行修复。

    使用io.StringIO的另一个优点是对通用换行符的支持。如果将关键字参数newline=''传递到io.StringIO中,它将能够在任何\n\r\n\r上拆分行。我发现StringIO.StringIO特别容易出现\r的问题。

    请注意,如果从six导入BytesIOStringIO,则在Python 2.x中会得到StringIO.StringIO,而在Python 3.x中会得到适当的类。如果您同意我前面几段的评估,这实际上是一个应该避免使用six,而应该直接从io导入的情况。

  • 如果您支持Python 2.5或更低版本和3.x: 您将需要StringIO.StringIO用于2.5或更低版本,因此最好使用six。但请注意,通常很难同时支持2.5和3.x,因此如果可能的话,应将最低支持版本提高到2.6。


26

感谢您的问题,感谢Roman提供答案。我花了一些时间才找到这个信息,希望以下内容对其他人有所帮助。

Python 2.7

参见:https://docs.scipy.org/doc/numpy/user/basics.io.genfromtxt.html

import numpy as np
from StringIO import StringIO

data = "1, abc , 2\n 3, xxx, 4"

print type(data)
"""
<type 'str'>
"""

print '\n', np.genfromtxt(StringIO(data), delimiter=",", dtype="|S3", autostrip=True)
"""
[['1' 'abc' '2']
 ['3' 'xxx' '4']]
"""

print '\n', type(data)
"""
<type 'str'>
"""

print '\n', np.genfromtxt(StringIO(data), delimiter=",", autostrip=True)
"""
[[  1.  nan   2.]
 [  3.  nan   4.]]
"""

Python 3.5:

import numpy as np
from io import StringIO
import io

data = "1, abc , 2\n 3, xxx, 4"
#print(data)
"""
1, abc , 2
 3, xxx, 4
"""

#print(type(data))
"""
<class 'str'>
"""

#np.genfromtxt(StringIO(data), delimiter=",", autostrip=True)
# TypeError: Can't convert 'bytes' object to str implicitly

print('\n')
print(np.genfromtxt(io.BytesIO(data.encode()), delimiter=",", dtype="|S3", autostrip=True))
"""
[[b'1' b'abc' b'2']
 [b'3' b'xxx' b'4']]
"""

print('\n')
print(np.genfromtxt(io.BytesIO(data.encode()), delimiter=",", autostrip=True))
"""
[[  1.  nan   2.]
 [  3.  nan   4.]]
"""

提示:

dtype="|Sx",其中x = {1, 2, 3, ...} 中的任意数字:

dtypes. Difference between S1 and S2 in Python

"|S1和|S2字符串是数据类型描述符;第一个表示数组包含长度为1的字符串,第二个表示长度为2的字符串。..."


24

您可以使用六(six)模块中的 StringIO

import six
import numpy

x = "1 3\n 4.5 8"
numpy.genfromtxt(six.StringIO(x))

7
为了让来自这里的示例与Python 3.5.2兼容,您可以按以下方式进行重写:
import io
data =io.BytesIO(b"1, 2, 3\n4, 5, 6") 
import numpy
numpy.genfromtxt(data, delimiter=",")

更改的原因可能是文件内容以数据(字节)的形式存在,直到以某种方式解码后才会成为文本。可能使用genfrombytesgenfromtxt更合适。

2

这里是关于 Python 3 的另一个例子。它将使用两个函数来相加两个数字,然后使用 CProfile 来保存 .prof 文件。然后,它将使用 pstats.Stats 和 ```StringIO`` 将保存的文件加载并将数据转换为字符串以供进一步使用。

main.py

import cProfile
import time
import pstats
from io import StringIO

def add_slow(a, b):
    time.sleep(0.5)
    return a+b

def add_fast(a, b):
    return a+b

prof = cProfile.Profile()

def main_func():
    arr = []
    prof.enable()
    for i in range(10):
        if i%2==0:
            arr.append(add_slow(i,i))
        else:
            arr.append(add_fast(i,i))
    prof.disable()
    #prof.print_stats(sort='time')
    prof.dump_stats("main_funcs.prof")
    return arr

main_func()
stream = StringIO();
stats = pstats.Stats("main_funcs.prof", stream=stream); 
stats.print_stats()
stream.seek(0)
print(16*'=',"RESULTS",16*'=')
print (stream.read())

使用方法:

python3 main.py

输出:

================ RESULTS ================
Tue Jul  6 17:36:21 2021    main_funcs.prof

         26 function calls in 2.507 seconds

   Random listing order was used

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       10    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        5    2.507    0.501    2.507    0.501 {built-in method time.sleep}
        5    0.000    0.000    2.507    0.501 profiler.py:39(add_slow)
        5    0.000    0.000    0.000    0.000 profiler.py:43(add_fast)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

注释:我们可以观察到在上述代码中,time.sleep函数花费了约2.507秒。


-1

我希望这能满足您的要求

import PyPDF4
import io

pdfFile = open(r'test.pdf', 'rb')
pdfReader = PyPDF4.PdfFileReader(pdfFile)
pageObj = pdfReader.getPage(1)
pagetext = pageObj.extractText()

for line in io.StringIO(pagetext):
    print(line)

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