使用Python中的正则表达式对数据进行分组

Wen*_*enT 8 python regex

我有一些像这样的原始数据:

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
Run Code Online (Sandbox Code Playgroud)

数据规则是:

  • 不是每个人都会从"亲爱的"开始,而如果有的话,它必须以成本结束

  • 该项目可能并不总是正常的单词,可以无限制地写入(包括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()
Run Code Online (Sandbox Code Playgroud)

现在输出看起来像:

('John', '1', 'Coke', '10')
('Ivan', '20', 'Milk', '')
('Tina', '10', 'Coke', '100')
('Mary', '5', 'Milk', '')
Run Code Online (Sandbox Code Playgroud)

上面显示的是我想要的.但是,如果item被一些奇怪的字符串替换A1~A10,有些输出会得到错误的信息:

('Ivan', '20', 'A1', '10')
('Mary', '5', 'A1', '10')
Run Code Online (Sandbox Code Playgroud)

我认为常量格式item field是它总是以,(如果有的话)结束.但我只是不知道如何利用这一优势.

认为通过使用上面的代码暂时成功,我认为(?P<item>\w+)必须被替换为(?P<item>.+).如果我这样做,它将在元组中使用错误的字符串,如:

('John', '1', 'Coke, cost 10 dollars', '')
Run Code Online (Sandbox Code Playgroud)

如何通过在Python中使用正则表达式将数据读入我想要的格式?

Jua*_*doy 5

我会用这个regex:

r'\s+(?P<name>\w+)\D*(?P<num>\d+)\sof\s(?P<item>[^,]+)(?:,\D+)?(?P<costs>\d+)?'
Run Code Online (Sandbox Code Playgroud)

演示

>>> 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')
Run Code Online (Sandbox Code Playgroud)

说明

你的正则表达式的第一部分是完美的,这是棘手的部分:

(?P<item>[^,]+)由于我们确定 当成本字符串存在时字符串将包含逗号,这里我们说除了逗号之外我们还需要设置项值的任何内容.

(?:,\D+)?(?P<costs>\d+)?我们在这里使用两组.在重要的事情?括号包围组:

'?' 使得到的RE匹配前面RE的0或1次重​​复.AB?将匹配'a'或'ab'.

所以我们? 用来匹配两种可能性(成本字符串是否存在)

(?:,\D+)是一个非捕获,它将匹配逗号,后跟除数字之外的任何内容.

(?P<costs>\d+)将捕获的命名组数字的任何费用.

  • 完成;)@ SuperBiasedMan (2认同)

sai*_*arm 5

我试过这个正则表达式

^(Dear)?\s*(?P<name>\w*)\D*(?P<num>\d+)\sof\s(?P<drink>\w*)(,\D*(?P<cost>\d+)\D*)?

说明

  1. ^(Dear)?匹配行以Dearif存在开始
  2. (?P<name>\w*) 用于捕获名称的名称捕获组
  3. \D* 匹配任何非数字字符
  4. (?P<num>\d+)命名捕获组来获取num.
  5. \sof\s 匹配字符串 of
  6. (?P<drink>\w*) 得到饮料
  7. (,\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]*)?')
Run Code Online (Sandbox Code Playgroud)

第一个数据片段

>>> 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')
Run Code Online (Sandbox Code Playgroud)

第二个数据片段

>>> 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)
Run Code Online (Sandbox Code Playgroud)


Cas*_*yte 5

没有正则表达式:

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)
Run Code Online (Sandbox Code Playgroud)

在产品名称之前,除了空格之外,它并不关心使用哪些字符,这就是为什么每行由5个部分中的空格分割的原因.当行以"Dear"开头时,最后一部分用逗号分隔以提取产品名称和价格.请注意,如果价格总是在同一个地方(即:在"成本"之后),您可以避免最内层的for循环并将其替换为price = tmp[1].split()[1]

注意:如果要防止处理空行,可以将第一个for循环更改为:

for line in (x for x in f if x.rstrip()):
Run Code Online (Sandbox Code Playgroud)