我希望能够在Python中读取输入文件中的数据,类似于Fortran处理列表导向读取的方式(即read (file, *) char_var, float_var, int_var
)。
棘手的部分是,就输入格式而言,Fortran处理这样的read语句的方式非常“宽容”。例如,使用前面的语句,这个:
"some string" 10.0, 5
将与以下内容相同:
"some string", 10.0
5
和此:
"other string", 15.0 /
的读法与:
相同"other string"
15
/
,int_var
的值保留与read语句之前相同的值。还是比较棘手的是:
"nother string", , 7
会将值分配给char_var
和int_var
,但float_var
会保留与read语句之前相同的值。
有没有一种优雅的方式来实现它?
答案 0 :(得分:2)
这确实很棘手 - 我发现写一个纯python基于声明的标记化器比思考正则表达式解析每一行更容易(很难做到)。
我已使用link provided by Vladimir作为规范 - 令牌系统已经通过了一些doctests。
def tokenize(line, separator=',', whitespace="\t\n\x20", quote='"'):
"""
>>> tokenize('"some string" 10.0, 5')
['some string', '10.0', '5']
>>> tokenize(' "other string", 15.0 /')
['other string', '15.0', '/']
>>> tokenize('"nother string", , 7')
['nother string', '', '7']
"""
inside_str = False
token_started = False
token = ""
tokens = []
separated = False
just_added = False
for char in line:
if char in quote:
if not inside_str:
inside_str = True
else:
inside_str = False
tokens.append(token)
token = ""
just_added = True
continue
if char in (whitespace + separator) and not inside_str:
if token:
tokens.append(token)
token = ""
just_added = True
elif char in separator:
if not just_added:
tokens.append("")
just_added = False
continue
token += char
if token:
tokens.append(token)
return tokens
class Character(object):
def __init__(self, length=None):
self.length = length
def __call__(self, text):
if self.length is None:
return text
if len(text) > self.length:
return text[:self.length]
return "{{:{}}}".format(self.length).format(text)
def make_types(types, default_value):
return types, [default_value] * len[types]
def fortran_reader(file, types, default_char="/", default_value=None, **kw):
types, results = make_types(types, default_value)
tokens = []
while True:
tokens = []
while len(tokens) < len(results):
try:
line = next(file)
except StopIteration:
raise StopIteration
tokens += tokenize(line, **kw)
for i, (type_, token) in enumerate(zip(types, tokens)):
if not token or token in default_char:
continue
results[i] = type_(token)
changed_types = yield(results)
if changed_types:
types, results = make_types(changed_types)
我没有仔细考虑过这个 - 但是对于标记器 -
它被设计为在Python for
语句中工作,如果相同的字段一遍又一遍地重复 - 或者它可以与Python的迭代器send
方法一起使用来更改要读取的值在每次迭代。
请测试,并通过电子邮件发送给我(我的个人资料中的地址)一些测试文件。如果确实没有类似的东西,也许这值得一些抛光并在Pypi上发表。
答案 1 :(得分:1)
由于我无法找到解决此问题的方法,我决定编写自己的解决方案。
主要驱动程序是阅读器类和标记器。读者一次从文件中获取一行,将其传递给tokenizer,然后分配给它的变量,根据需要获取下一行。
class FortranAsciiReader(file):
def read(self, *args):
"""
Read from file into the given objects
"""
num_args = len(args)
num_read = 0
encountered_slash = False
# If line contained '/' or read into all varialbes, we're done
while num_read < num_args and not encountered_slash:
line = self.readline()
if not line:
raise Exception()
values = tokenize(line)
# Assign elements one-by-one into args, skipping empty fields and stopping at a '/'
for val in values:
if val == '/':
encountered_slash = True
break
elif val == '':
num_read += 1
else:
args[num_read].assign(val)
num_read += 1
if num_read == num_args:
break
标记生成器根据Fortran执行列表定向读取的方式将行拆分为标记,其中&#39;,&#39;和空格是分隔符,标记可能是重复的&#34;通过4*token
和/或终止输入。
我在这里重现了tokenizer的实现有点长,而且我还包括透明地提供基本Fortran内部类型(即Real,Character,Integer等)功能的类。整个项目可以在我的github帐户上找到,目前位于https://github.com/bprichar/PyLiDiRe。感谢jsbueno获取tokenizer的灵感。