Python相当于Fortran列表导向的输入

时间:2016-03-22 20:56:45

标签: python format fortran ascii

我希望能够在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_varint_var,但float_var会保留与read语句之前相同的值。

有没有一种优雅的方式来实现它?

2 个答案:

答案 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的灵感。