除了所有输入数据外,还输出一个 re.findall() 到 CSV

Output a re.findall() to CSV in addition to all input data

我正在尝试将正则表达式 .findall() 搜索的结果保存到 csv 中,但在将结果附加到输出文件时遇到困难。

因为我对 Python 还是很陌生,所以我试图将这个问题限制在仅使用 csv 和 re 库 - 但如果有更简单的方法(即在 pandas 中)这也将有助于了解。


  1. 如何将输入 CSV 的全部内容复制到输出 CSV 并将邮政编码/找到的正则表达式添加到找到它的行?

  2. 是否有任何明显的错误检查形式或其他我遗漏的东西?

  3. 是否有更好的方法将输入 CSV 的 header 自动添加到输出 CSV 而无需明确指定它们?

  4. 是否可以使用 DictWriter 来做到这一点?正如我最初尝试的那样。


import csv, re

pattern = r'[A-Z]{1,2}[0-9R][0-9A-Z]?[0-9][A-Z]{2}'
postcodes = []
with open(r'Postcode/addressin.csv', 'r') as csvinput:
    csv_reader = csv.DictReader(csvinput)

    with open(r'Postcode/addressout.csv', 'w', newline='') as csvoutput:
        fieldnames = ['Address', 'Name']
        csv_writer = csv.writer(csvoutput)

        csv_writer.writerow(fieldnames)

        for line in csv_reader:
            postcodes = re.findall(pattern, line["Address"])
            csv_writer.writerow(postcodes)

示例数据:

Address,Name,Lat,Long,2016 Sales,Type
48  Park Avenue, LATTON, SN6 4SZ,Nikki Yellowbeard,-23.17549,36.74641,9727,AA
IV21 1TD 116  Walwyn Rd CHARLESTOWN,Jonh Doe,-10.98309,156.41854,11932,AE

您最好将输入的 csv 文件读取到数据框中,然后使用 pandas.str.extract() 从地址栏中提取 post 代码。

  1. 读取 csv:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html
  2. 提取post代码:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.str.extract.html
  3. 写入 csv:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_csv.html

在我看来,第一个字段地址中的逗号会造成不规则,我不太确定绕过这些逗号的最佳方法是什么,但是这个表达式:

(.*),(.*),\s*([0-9.-]+)\s*,\s*([0-9.]+)\s*,([0-9]{4,5}(?:-[0-9]{4})?)\s*,\s*([A-Z]{2})

可能是一种研究方法。


Demo


美国邮政编码通常采用以下格式:

([0-9]{5}(?:-[0-9]{4})?)

只是为了演示,我已经包括:

[0-9]{4,5}

您可以简单地删除它。

例子

import re

regex = r"(.*),(.*),\s*([0-9.-]+)\s*,\s*([0-9.]+)\s*,([0-9]{4,5}(?:-[0-9]{4})?)\s*,\s*([A-Z]{2})"

test_str = ("Address,Name,Lat,Long,2016 Sales,Type\n"
    "48  Park Avenue, LATTON, SN6 4SZ,Nikki Yellowbeard,-23.17549,36.74641,9727,AA\n"
    "IV21 1TD 116  Walwyn Rd CHARLESTOWN,Jonh Doe,-10.98309,156.41854,11932,AE")

matches = re.finditer(regex, test_str, re.MULTILINE)

for matchNum, match in enumerate(matches, start=1):
    
    print ("Match {matchNum} was found at {start}-{end}: {match}".format(matchNum = matchNum, start = match.start(), end = match.end(), match = match.group()))
    
    for groupNum in range(0, len(match.groups())):
        groupNum = groupNum + 1
        
        print ("Group {groupNum} found at {start}-{end}: {group}".format(groupNum = groupNum, start = match.start(groupNum), end = match.end(groupNum), group = match.group(groupNum)))

如果我们不验证值,那么只需这个表达式

(.*),(.*),(.*),(.*),(.*),(.*)

可能会奏效。

Demo

您样本中的 CSV 无效;看起来您在地址字段周围缺少引号。

此外,re.findall() 可以 return 多个结果 - CSV 不能真正在一列中容纳多个值(当你尝试时,你会陷入混乱试图摆脱现在);一般来说,一个更好的解决方案是规范化您的数据,以便每个字段都包含最小的原子数据,这些数据不能进一步划分为更小的信息单元。

如果您尝试表示嵌套或分层数据,可以查看 JSON 或 XML 而不是 CSV 作为您的存储格式。

除此之外,这里是一个重构,它在每一行的末尾添加一个字段,并嵌入一个 semicolon-separated 列表 post 代码(或者什么都没有,如果正则表达式匹配不成功)到该字段。

import csv, re

# Precompile the pattern
pattern = reccompile(r'[A-Z]{1,2}[0-9R][0-9A-Z]?[0-9][A-Z]{2}')

with open(r'Postcode/addressin.csv', 'r') as csvinput, open(r'Postcode/addressout.csv', 'w') as csvoutput:
    csv_reader = csv.DictReader(csvinput)
    csv_writer = csv.writer(csvoutput)

    outputfieldnames = ['Address', 'Name', 'Postcode']
    csv_writer.writerow(outputfieldnames)

    for line in csv_reader:
        postcodes = ';'.join(pattern.findall(line["Address"]))
        csv_writer.writerow([line["Address"], line["Name"], postcodes])