数据解析Python中的正则表达式

时间:2014-03-18 02:45:51

标签: python regex parsing

我相对于正则表达式,我们对它们的强大程度感到惊讶。我有这个项目,并想知道正则表达式是否合适以及如何使用它们。

在这个项目中,我收到了一个包含大量数据的文件。这是一点点:

* File "miles.dat" from the Stanford GraphBase (C) 1993 Stanford University
* Revised mileage data for highways in the United States and Canada, 1949
* This file may be freely copied but please do not change it in any way!
* (Checksum parameters 696,295999341)

Youngstown, OH[4110,8065]115436
Yankton, SD[4288,9739]12011
966
Yakima, WA[4660,12051]49826
1513 2410

它有一个城市名称和州,然后是括号中的纬度和经度,然后是人口。在下一行中,从该城市到数据中前面列出的每个城市的距离。数据继续在180个城市进行。

我的工作是创建4个列表。一个用于城市,一个用于坐标,一个用于人口,一个用于城市之间的距离。我知道如果没有正则表达式(我已经写过),这是可能的,但是代码很笨重而且效率不高。您认为最好的方法是什么?

2 个答案:

答案 0 :(得分:1)

我建议使用城市线的正则表达式和距离的列表理解(正则表达式也是过度杀伤和慢速)。

这样的东西
import re

CITY_REG = re.compile(r"([^[]+)\[([0-9.]+),([0-9.]+)\](\d+)")
CITY_TYPES = (str, float, float, int)

def get_city(line):
    match = CITY_REG.match(line)
    if match:
        return [type(dat) for dat,type in zip(match.groups(), CITY_TYPES)]
    else:
        raise ValueError("Failed to parse {} as a city".format(line))

def get_distances(line):
    return [int(i) for i in line.split()]

然后

>>> get_city("Youngstown, OH[4110.83,8065.14]115436")
['Youngstown, OH', 4110.83, 8065.14, 115436]

>>> get_distances("1513 2410")
[1513, 2410]

你可以像

一样使用它
# This code assumes Python 3.x
from itertools import count, zip_longest

def file_data_lines(fname, comment_start="* "):
    """
    Return lines of data from file
     (strip out blank lines and comment lines)
    """
    with open(fname) as inf:
        for line in inf:
            line = line.rstrip()
            if line and not line.startswith(comment_start):
                yield line

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
    args = [iter(iterable)] * n
    return zip_longest(fillvalue=fillvalue, *args)

def city_data(fname):
    data = file_data_lines(fname)

    # city 0 has no distances line
    city_line = next(data)
    city, lat, lon, pop = get_city(city_line)
    yield city, (lat, lon), pop, []

    # all remaining cities
    for city_line, dist_line in grouper(data, 2, ''):
        city, lat, lon, pop = get_city(city_line)
        dists = get_distances(dist_line)
        yield city, (lat, lon), pop, dists

最后

def main():
    # load per-city data
    city_info = list(city_data("miles.dat"))
    # transpose into separate lists
    cities, coords, pops, dists = list(zip(*city_info))

if __name__=="__main__":
    main()

修改

工作原理:

CITY_REG = re.compile(r"([^[]+)\[([0-9.]+),([0-9.]+)\](\d+)")

[^[]匹配除[以外的任何字符;因此([^[]+)获取一个或多个字符(但不包括)第一个[;获取"城市名称,州",并将其作为第一组返回。

\[匹配文字[字符;我们必须用斜线来逃避它,以明确我们没有开始另一个角色组。

[0-9.]匹配0,1,2,3,... 9或句点字符。所以([0-9.]+)得到一个或多个数字或句点 - 即任何整数或浮点数,不包括尾数 - 并将其作为第二组返回。这是受限制的 - 它会接受类似0.1.2.3的东西,它不是一个有效的浮点数 - 但匹配有效浮点数的表达式会更加复杂,这是假设我们不会遇到异常输入,那么就足够了。

我们得到逗号,将另一个数字作为第3组匹配,得到结束方括号;然后\d匹配任何数字(与[0-9]相同),因此(\d+)匹配一个或多个数字,即整数,并将其作为第四组返回。

match = CITY_REG.match(line)

我们针对一行输入运行正则表达式;如果匹配,我们会返回包含匹配数据的Match对象,否则我们会得到None

if match:

......这是一种简短的说法if bool(match) == Truebool(MyClass)始终为True(除非明确重写,即对于空列表或词组),bool(None)始终为False,因此有效且#34;如果正则表达式成功匹配字符串:"。

CITY_TYPES = (str, float, float, int)

正则表达式只返回字符串;你想要不同的数据类型,所以我们必须转换,这是什么

[type(dat) for dat,type in zip(match.groups(), CITY_TYPES)]

确实; match.groups()是四个匹配的数据,CITY_TYPES是每个匹配数据的所需数据类型,因此zip(data, types)会返回[("Youngstown, OH", str), ("4110.83", float), ("8065.14", float), ("115436", int)]之类的内容。然后,我们将数据类型应用于每个部分,最后得到["Youngstown, OH", 4110.83, 8065.14, 115436]

希望有所帮助!

答案 1 :(得分:0)

S = """Youngstown, OH[4110,8065]115436
    Yankton, SD[4288,9739]12011
    966
    Yakima, WA[4660,12051]49826
    1513 2410"""

import re

def get_4_list():
    city_list = []
    coordinate_list = []
    population_list = []
    distance_list = []

    line_list = S.split('\n')
    line_pattern = re.compile(r'(\w+).+(\[[\d,]+\])(\d+)')
    for each_line in line_list:
        match_list = line_pattern.findall(each_line)
        if match_list:
            print match_list
            city_list.append(match_list[0][0])
            coordinate_list.append(match_list[0][1])
            population_list.append(match_list[0][2])
        else:
            distance_list.extend(each_line.split())

    return city_list, coordinate_list, population_list, distance_list