与正则表达式找到正确匹配的问题

Problem with finding the correct match with regex

我有一些数据,正在尝试处理。基本上我想将所有逗号 , 更改为分号 ;,但某些字段包含文本、用户名或密码也包含逗号。如何更改除 "?

中包含的逗号之外的所有逗号

测试数据:

Secret Name,URL,Username,Password,Notes,Folder,TOTP Key,TOTP Backup Codes
test1,,username,"pass,word",These are the notes,\Some\Folder,,
test2,,"user1, user2, user3","pass,word","Hello, I'm mr Notes",\Some\Folder,,
test3,http://1.2.3.4/ucsm/ucsm.jnlp,"xxxx\n(use Drop down, select Hello)",password,Use the following\nServer1\nServer2,\Some\Folder,,

我尝试了什么?

secrets = """Secret Name,URL,Username,Password,Notes,Folder,TOTP Key,TOTP Backup Codes
test1,,username,"pass,word",These are the notes,\Some\Folder,,
test2,,"user1, user2, user3","pass,word","Hello, I'm mr Notes",\Some\Folder,,
test3,http://1.2.3.4/ucsm/ucsm.jnlp,"xxxx\n(use Drop down, select Hello)",password,Use the following\nServer1\nServer2,\Some\Folder,,
"""

test = re.findall(r'(.+?\")(.+)(\".+)', secrets)

for line in test:
    part1, part2, part3 = line
    processed = "".join([part1.replace(",", ";"), part2, part3.replace(",", ";")])
    print(processed)

结果:

test1;;username;"pass,word";These are the notes;\Some\Folder;;
test2;;"user1, user2, user3","pass,word","Hello, I'm mr Notes";\Some\Folder;;

它工作正常,当行中只出现一次 "" 并且没有换行符时,但是当有更多或引号中有换行符时,它被打破了。我该如何解决这个问题?

仅供参考:注释可以包含多个换行符。

我相信应该是这样的:

import re
print( re.sub(r'("[^"]*")|,', lambda x: x.group(1) if x.group(1) else x.group().replace(",", ";"), secrets))

这里不需要正则表达式,利用 CSV 解析器:

import csv, io

inp = csv.reader(io.StringIO(secrets), # or use file as input
                 quotechar='"', delimiter=',', quoting=csv.QUOTE_ALL)
with open('out.csv', 'w') as out:
    csv.writer(out, delimiter=';').writerows(inp)

输出文件:

Secret Name;URL;Username;Password;Notes;Folder;TOTP Key;TOTP Backup Codes
test1;;username;pass,word;These are the notes;\Some\Folder;;
test2;;user1, user2, user3;pass,word;Hello, I'm mr Notes;\Some\Folder;;
test3;http://1.2.3.4/ucsm/ucsm.jnlp;"xxxx
(use Drop down, select Hello)";password;Use the following
Server1
Server2;\Some\Folder;;

可选地,使用 csv.writer 中的 quoting=csv.QUOTE_ALL 参数。

mozway 的解决方案看起来是解决这个问题的最佳方法,但有趣的是,SM1312 的正则表达式与 sub 函数的更简单的替换参数(即 r';')几乎完美地工作:

import re
print (re.sub(r'("[^"]*")|,', r';', secrets))

唯一的问题是在引用条目后引入了一个额外的分号。发生这种情况是因为第一个交替成员(即 ("[^"]*"))不使用逗号,但无论哪个交替成员匹配,替换参数都会添加一个分号。只需向第一个交替成员添加一个逗号即可解决此问题,并且非常适合示例数据:

import re
print (re.sub(r'("[^"]*"),|,', r';', secrets))

但是,如果数据包含引用条目作为数据的最后一列(即 TOTP 备份代码),则失败;最后引用的条目中的任何逗号都将更改为分号。这可能不是可接受的故障模式,因为它正在更改数据集。以下解决了该问题,但引入了一个可以容忍的不同错误;它在行尾添加了一个额外的分号:

import re
print (re.sub(r'("[^"]*")(,|(?=\s+))|,', r';', secrets))

这是通过将原始交替成员的第一部分更改为使用交替本身来实现的。也就是说,在引用的条目之后与逗号匹配的部分更改为除了空格(即 (,|(?=\s+)))之外什么都不检查,其中包括行尾,在引用的条目之后使用遵循积极的先行断言:(?=\s+)。使用空白的正先行断言而不是简单地匹配空白以避免消耗空白并将其从结果输出中消除。