Python流提取

5 投票
2 回答
1186 浏览
提问于 2025-04-17 16:03

很多编程语言的标准库里都有一个“扫描器API”,可以从文本输入流中提取字符串、数字或其他对象。(比如,Java有Scanner类,C++有istream,C有scanf。)

那在Python中有什么类似的东西呢?

Python有一个流接口,也就是一些类是从io.IOBase继承来的。不过,Python的TextIOBase流接口只提供了处理按行输入的功能。看完文档和在谷歌搜索后,我找不到Python标准模块里有能让我提取文本流中的整数,或者提取下一个以空格分隔的单词作为字符串的东西。有没有什么标准的方法可以做到这些呢?

2 个回答

1

我所知道的,没有直接对应的东西。不过,你可以用正则表达式做到差不多的效果(可以看看re模块)。

举个例子:

# matching first integer (space delimited)
re.match(r'\b(\d+)\b',string)

# matching first space delimited word
re.match(r'\b(\w+)\b',string)

# matching a word followed by an integer (space delimited)
re.match(r'\b(\w+)\s+(\d+)\b',string)

这比一般的C语言风格的扫描器接口需要多一点工作,但它也非常灵活和强大。不过,你需要自己处理流输入输出。

3

在Python中,没有像fscanf或者Java的Scanner那样的功能。最简单的解决办法是让用户输入以换行符分隔的数据,而不是用空格分隔。这样你就可以逐行读取,然后把每一行转换成正确的数据类型。

如果你希望用户提供更有结构的数据,那你可能需要为用户输入创建一个解析器。Python有一些很不错的解析库,比如pyparsing。还有一个scanf模块,虽然它最后一次更新是在2008年。

如果你不想依赖外部库,那么你可以使用正则表达式来匹配输入的内容。正则表达式确实需要在字符串上工作,但你可以通过分块读取来轻松解决这个问题。比如,下面的代码通常能很好地工作:

import re


FORMATS_TYPES = {
    'd': int,
    'f': float,
    's': str,
}


FORMATS_REGEXES = {    
    'd': re.compile(r'(?:\s|\b)*([+-]?\d+)(?:\s|\b)*'),
    'f': re.compile(r'(?:\s|\b)*([+-]?\d+\.?\d*)(?:\s|\b)*'),
    's': re.compile(r'\b(\w+)\b'),
}


FORMAT_FIELD_REGEX = re.compile(r'%(s|d|f)')


def scan_input(format_string, stream, max_size=float('+inf'), chunk_size=1024):
    """Scan an input stream and retrieve formatted input."""

    chunk = ''
    format_fields = format_string.split()[::-1]
    while format_fields:
        fields = FORMAT_FIELD_REGEX.findall(format_fields.pop())
        if not chunk:
            chunk = _get_chunk(stream, chunk_size)

        for field in fields:
            field_regex = FORMATS_REGEXES[field]
            match = field_regex.search(chunk)
            length_before = len(chunk)
            while match is None or match.end() >= len(chunk):
                chunk += _get_chunk(stream, chunk_size)
                if not chunk or length_before == len(chunk):
                    if match is None:
                        raise ValueError('Missing fields.')
                    break
            text = match.group(1)
            yield FORMATS_TYPES[field](text)
            chunk = chunk[match.end():]



def _get_chunk(stream, chunk_size):
    try:
        return stream.read(chunk_size)
    except EOFError:
        return ''

示例用法:

>>> s = StringIO('1234 Hello World -13.48 -678 12.45')
>>> for data in scan_input('%d %s %s %f %d %f', s): print repr(data)
...                                                                                            
1234                                                                                           
'Hello'
'World'
-13.48
-678
12.45

你可能需要对这个进行扩展,并进行适当的测试,但这应该能给你一些思路。

撰写回答