我相对于正则表达式,我们对它们的强大程度感到惊讶。我有这个项目,并想知道正则表达式是否合适以及如何使用它们。
在这个项目中,我收到了一个包含大量数据的文件。这是一点点:
* 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个列表。一个用于城市,一个用于坐标,一个用于人口,一个用于城市之间的距离。我知道如果没有正则表达式(我已经写过),这是可能的,但是代码很笨重而且效率不高。您认为最好的方法是什么?
答案 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) == True
。 bool(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