【问题标题】:Python Stream ExtractionPython 流提取
【发布时间】:2013-02-01 02:05:24
【问题描述】:

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

在 Python 中 this 的等价物是什么?

Python 有一个流接口,即继承自 io.IOBase 的类。但是,Python TextIOBase 流接口仅提供面向行的输入的功能。在reading the documentationsearching on Google 之后,我在标准Python 模块中找不到可以让我从文本流中提取整数或将下一个以空格分隔的单词提取为字符串的内容。是否有任何标准设施可以做到这一点?

【问题讨论】:

  • 如果输入很脏/随意,我倾向于使用re模块;如果输入是结构化的,我更喜欢 simpleparse 之类的解析器库(EBNL 比正则表达式更容易维护)。
  • 如果您确实有一个用例,也许更新您的问题而不是进行一般查询会更有效率。
  • re 模块可用于专门模拟sscanf - 但是它并不真正等同于scanf 系列函数。 scanf 系列函数是面向流的,而 re 是面向 string 的。例如,fscanf 会自动从文件输入流中逐个字符地读取,而re 则需要预先形成完整的字符串。
  • @Channel72 除非你先mmap 文件(因此像一个非常大的字符串一样尝试它而不将其加载到内存),然后使用re.finditer (可能保持一些位置,通过如果需要,可以到另一个查找器,或其他任何

标签: python python-3.x stream scanf


【解决方案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 风格扫描仪界面需要更多的工作,但它也非常灵活和强大。不过,您必须自己处理流 I/O。

【讨论】:

    【解决方案2】:

    没有 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
    

    您可能需要扩展它,并对其进行适当的测试,但它应该会给您一些想法。

    【讨论】:

      猜你喜欢
      • 2010-09-30
      • 2020-05-17
      • 1970-01-01
      • 1970-01-01
      • 2014-11-25
      • 1970-01-01
      • 1970-01-01
      • 2012-11-13
      • 1970-01-01
      相关资源
      最近更新 更多