可扩展的固定宽度文件处理模块
项目描述
xfw是一个可扩展的固定宽度文件处理模块。
功能
字段类型(整数、字符串、日期)独立于文件结构声明,可以通过子类扩展。(BaseField子类)
多字段结构声明(FieldList类)
非均匀文件结构声明(FieldListFile)
校验和/哈希计算辅助程序(ChecksumedFile子类)
不依赖于行概念(文件中可能不包含连续字段集之间的CR/LF字符)
缺失的功能/错误
字符串截断不受多字节(UTF-8等)影响,并且会毫无顾忌地在任何实体中间截断。如果您的字段以某种编码的字节数定义,只需向xfw提供Unicode对象,并在其外部进行转码。请参阅《codecs》标准模块。
适当的接口声明
字段(IntegerField,DateTimeField)在解析时应默认转换
FieldList总长度应可选,并且仅在字段长度之和更长时用于在记录末尾自动生成匿名填充
示例
免责声明:给出的文件格式纯粹是假设性的,并非来自我所知的任何规范,不应作为指南,而只是作为xfw功能的展示。
让我们假设一个文件由一个通用头部组成,包含一些常值5字符标识符,一个3字符整数给出记录数量,以及一个可选的20字符注释。它后面是记录,记录本身由一个日期(YYYYMMDD)、行类型(2字符整数)和行数(2字符整数)组成,后面是行。所有行类型都以时间(HHMMSS)开始,后面是依赖于行类型的字段
类型1:一个10字符字符串
类型2:一个2字符整数,8个字符的填充,一个1字符整数
要作为doctest运行以下代码,请运行
python -m doctest README.rst
声明所有文件结构
>>> import xfw >>> ROOT_HEADER = xfw.FieldList([ ... (xfw.StringField(5), True, 'header_id'), ... (xfw.IntegerField(3, cast=True), True, 'block_count'), ... (xfw.StringField(15), False, 'comment'), ... ], 23, fixed_value_dict={ ... 'header_id': 'HEAD1', ... }) >>> BLOCK_HEADER = xfw.FieldList([ ... (xfw.DateTimeField('%Y%m%d', cast=True), True, 'date'), ... (xfw.IntegerField(2, cast=True), True, 'row_type'), ... (xfw.IntegerField(2, cast=True), True, 'row_count'), ... ], 12) >>> ROW_BASE = xfw.FieldList([ ... (xfw.DateTimeField('%H%M%S', cast=True), True, 'time'), ... ], 6) >>> ROW_TYPE_DICT = { ... 1: xfw.FieldList([ ... ROW_BASE, ... (xfw.StringField(10), True, 'description'), ... ], 16), ... 2: xfw.FieldList([ ... ROW_BASE, ... (xfw.IntegerField(2, cast=True), True, 'some_value'), ... (xfw.StringField(8), False, None), # annonymous padding ... (xfw.IntegerField(1, cast=True), True, 'another_value'), ... ], 17), ... } >>> def blockCallback(head, item_list=None): ... if item_list is None: ... row_count = head['row_count'] ... else: ... row_count = len(item_list) ... return row_count, ROW_TYPE_DICT[head['row_type']] >>> FILE_STRUCTURE = xfw.ConstItemTypeFile( ... ROOT_HEADER, ... 'block_count', ... xfw.FieldListFile( ... BLOCK_HEADER, ... blockCallback, ... separator='\n', ... ), ... separator='\n', ... )
通过哈希辅助包装器(SHA1)解析示例文件
>>> from cStringIO import StringIO >>> sample_file = StringIO( ... 'HEAD1002blah \n' ... '201112260101\n' ... '115500other str \n' ... '201112260201\n' ... '11550099 8' ... ) >>> from datetime import datetime >>> checksumed_wrapper = xfw.SHA1ChecksumedFile(sample_file) >>> parsed_file = FILE_STRUCTURE.parseStream(checksumed_wrapper) >>> parsed_file == \ ... ( ... { ... 'header_id': 'HEAD1', ... 'block_count': 2, ... 'comment': 'blah', ... }, ... [ ... ( ... { ... 'date': datetime(2011, 12, 26, 0, 0), ... 'row_type': 1, ... 'row_count': 1, ... }, ... [ ... { ... 'time': datetime(1900, 1, 1, 11, 55), ... 'description': 'other str', ... }, ... ] ... ), ... ( ... { ... 'date': datetime(2011, 12, 26, 0, 0), ... 'row_type': 2, ... 'row_count': 1, ... }, ... [ ... { ... 'time': datetime(1900, 1, 1, 11, 55), ... 'some_value': 99, ... 'another_value': 8, ... }, ... ] ... ), ... ], ... ) True
验证SHA1是否正确累积
>>> import hashlib >>> hashlib.sha1(sample_file.getvalue()).hexdigest() == checksumed_wrapper.getHexDigest() True
从解析数据生成文件(如上所述已验证正确)
>>> generated_stream = StringIO() >>> FILE_STRUCTURE.generateStream(generated_stream, parsed_file) >>> generated_stream.getvalue() == sample_file.getvalue() True
同样,使用Unicode对象并产生不同长度的二进制流,尽管包含相同数量的实体。注意,格式声明中定义的固定值是可选的(例如:header_id),相关值是自动计算的(例如:block_count)。
使用适合单个UTF-8编码字节的Unicode字符生成
>>> import codecs >>> encoded_writer = codecs.getwriter('UTF-8') >>> input_data = ( ... { ... 'comment': u'Just ASCII', ... }, ... [], ... ) >>> sample_file = StringIO() >>> FILE_STRUCTURE.generateStream(encoded_writer(sample_file), input_data) >>> sample_file.getvalue() 'HEAD1000Just ASCII ' >>> len(sample_file.getvalue()) 23
再次生成,字符在编码时需要更多字节,并演示校验和生成
>>> wide_input_data = ( ... { ... 'comment': u'\u3042\u3044\u3046\u3048\u304a\u304b\u304d\u304f\u3051\u3053\u3055\u3057\u3059\u305b\u305d', ... }, ... [], ... ) >>> wide_sample_file = StringIO() >>> checksumed_wrapper = xfw.SHA1ChecksumedFile(wide_sample_file) >>> FILE_STRUCTURE.generateStream(encoded_writer(checksumed_wrapper), wide_input_data) >>> wide_sample_file.getvalue() 'HEAD1000\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a\xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93\xe3\x81\x95\xe3\x81\x97\xe3\x81\x99\xe3\x81\x9b\xe3\x81\x9d' >>> len(wide_sample_file.getvalue()) 53 >>> hashlib.sha1(wide_sample_file.getvalue()).hexdigest() == checksumed_wrapper.getHexDigest() True
尽管如此,两者解析为它们各自的原数据
>>> encoded_reader = codecs.getreader('UTF-8') >>> FILE_STRUCTURE.parseStream(encoded_reader(StringIO(sample_file.getvalue())))[0]['comment'] u'Just ASCII' >>> FILE_STRUCTURE.parseStream(encoded_reader(StringIO(wide_sample_file.getvalue())))[0]['comment'] u'\u3042\u3044\u3046\u3048\u304a\u304b\u304d\u304f\u3051\u3053\u3055\u3057\u3059\u305b\u305d'
项目详情
xfw-0.10.tar.gz的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 35563498e51701a3a600f916f9db7b4b0773ff5b6b6ef1809b2c95c2d5692a62 |
|
MD5 | 2f82e71bb2a55537ecbac6ddd262966b |
|
BLAKE2b-256 | 6a894e1d45fbce5c8bafcbdc6245e3c0a4c94d67b87c92c20b7b94cd6897ed09 |