[英]Grouping data with a regex in Python
我有一些像这样的原始数据:
Dear John Buy 1 of Coke, cost 10 dollars
Ivan Buy 20 of Milk
Dear Tina Buy 10 of Coke, cost 100 dollars
Mary Buy 5 of Milk
数据规则是:
不是每个人都会从“亲爱的”开始,而如果有的话,它必须以成本结束
该项目可能并不总是正常的单词,可以无限制地写入(包括str,num等)
我想分组信息,我试图使用正则表达式。 这就是我之前尝试过的:
for line in file.readlines():
match = re.search(r'\s+(?P<name>\w+)\D*(?P<num>\d+)\sof\s(?P<item>\w+)(?:\D+)(?P<costs>\d*)',line)
if match is not None:
print(match.groups())
file.close()
现在输出看起来像:
('John', '1', 'Coke', '10')
('Ivan', '20', 'Milk', '')
('Tina', '10', 'Coke', '100')
('Mary', '5', 'Milk', '')
上面显示的是我想要的。 但是,如果item
被一些奇怪的字符串替换为A1~A10
,则某些输出将获得错误的信息:
('Ivan', '20', 'A1', '10')
('Mary', '5', 'A1', '10')
我认为,在不断的格式item field
的是,它总是以结束,
(如果有的话)。 但我只是不知道如何利用这一优势。
认为通过使用上面的代码暂时成功,我认为(?P<item>\\w+)
必须像(?P<item>.+)
一样被替换。 如果我这样做,它将在元组中使用错误的字符串,如:
('John', '1', 'Coke, cost 10 dollars', '')
如何通过在Python中使用正则表达式将数据读入我想要的格式?
我会用这个regex
:
r'\s+(?P<name>\w+)\D*(?P<num>\d+)\sof\s(?P<item>[^,]+)(?:,\D+)?(?P<costs>\d+)?'
演示
>>> line = 'Dear Tina Buy 10 of A1~A10'
>>> match = re.search(r'\s+(?P<name>\w+)\D*(?P<num>\d+)\sof\s(?P<item>[^,]+)(?:,\D+)?(?P<costs>\d+)?', line)
>>> match.groups()
('Tina', '10', 'A1~A10', None)
>>> line = 'Dear Tina Buy 10 of A1~A10, cost 100 dollars'
>>> match = re.search(r'\s+(?P<name>\w+)\D*(?P<num>\d+)\sof\s(?P<item>[^,]+)(?:,\D+)?(?P<costs>\d+)?', line)
>>> match.groups()
('Tina', '10', 'A1~A10', '100')
说明
你的正则表达式的第一部分是完美的,这是棘手的部分:
(?P<item>[^,]+)
因为我们确定当成本字符串存在时字符串将包含逗号 ,这里我们说除了逗号之外我们还想要设置项目值。
(?:,\\D+)?(?P<costs>\\d+)?
我们在这里使用两组。 重要的是?
在括起组的括号后:
'?' 使得到的RE匹配前面RE的0或1次重复。 AB? 将匹配'a'或'ab'。
所以我们用?
匹配两种可能性( 成本字符串是否存在)
(?:,\\D+)
是一个非捕获 ,它将匹配逗号,后跟除数字之外的任何内容。
(?P<costs>\\d+)
将捕获在指定组的成本的任何数字。
我试过这个正则表达式
^(Dear)?\\s*(?P<name>\\w*)\\D*(?P<num>\\d+)\\sof\\s(?P<drink>\\w*)(,\\D*(?P<cost>\\d+)\\D*)?
说明
^(Dear)?
匹配行从Dear
开始,如果存在的话 (?P<name>\\w*)
用于捕获名称的名称捕获组 \\D*
匹配任何非数字字符 (?P<num>\\d+)
命名捕获组以获取num
。 \\sof\\s
匹配的字符串of
(?P<drink>\\w*)
来喝酒 (,\\D*(?P<cost>\\d+)\\D*)?
这是一个可选组,以获得饮料的成本 同
>>> reobject = re.compile('^(Dear)?\s*(\w*)[\sa-zA-Z]*(\d+)\s*\w*\s*(\w*)(,[\sa-zA-Z]*(\d+)[\s\w]*)?')
第一个数据片段
>>> data1 = 'Dear John Buy 1 of Coke, cost 10 dollars'
>>> match_object = reobject.search(data1)
>>> print (match_object.group('name') , match_object.group('num'), match_object.group('drink'), match_object.group('cost'))
('John', '1', 'Coke', '10')
第二个数据片段
>>> data2 = ' Ivan Buy 20 of Milk'
>>> match_object = reobject.search(data2)
>>> print (match_object.group('name') , match_object.group('num'), match_object.group('drink'), match_object.group('cost'))
('Ivan', '20', 'Milk', None)
没有正则表达式:
with open('commandes.txt') as f:
results = []
for line in f:
parts = line.split(None, 5)
price = ''
if parts[0] == 'Dear':
tmp = parts[5].split(',', 1)
for tok in tmp[1].split():
if tok.isnumeric():
price = tok
break
results.append((parts[1], parts[3], tmp[0], price))
else:
results.append((parts[0], parts[2], parts[4].split(',')[0], price))
print(results)
在产品名称之前,除了空格之外,它并不关心使用哪些字符,这就是为什么每行由5个部分中的空格分割的原因。 当行以“Dear”开头时,最后一部分用逗号分隔以提取产品名称和价格。 请注意,如果价格总是在同一个地方(即:在“成本”之后),您可以避开最里面的for循环并将其替换为price = tmp[1].split()[1]
注意:如果要防止处理空行,可以将第一个for循环更改为:
for line in (x for x in f if x.rstrip()):
如果你使用.+
,子模式将抓住整个行的其余部分.
匹配任何字符,但没有re.S
标志的换行符。
您可以将\\w+
替换为否定字符类子模式[^,]+
以匹配逗号以外的一个或多个字符:
r'\s+(?P<name>\w+)\D*(?P<num>\d+)\sof\s(?P<item>[^,]+)\D*(?P<costs>\d*)'
^^^^^
查看IDEONE演示 :
import re
file = "Dear John Buy 1 of A1~A10, cost 10 dollars\n Ivan Buy 20 of Milk\nDear Tina Buy 10 of Coke, cost 100 dollars\n Mary Buy 5 of Milk"
for line in file.split("\n"):
match = re.search(r'\s+(?P<name>\w+)\D*(?P<num>\d+)\sof\s(?P<item>[^,\W]+)\D*(?P<costs>\d*)',line)
if match:
print(match.groups())
输出:
('John', '1', 'A1~A10', '10')
('Ivan', '20', 'Mil', '')
('Tina', '10', 'Coke', '100')
('Mary', '5', 'Mil', '')
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.