除了所有输入数据之外,将 re.findall() 输出到 CSV

Posted

技术标签:

【中文标题】除了所有输入数据之外,将 re.findall() 输出到 CSV【英文标题】:Output a re.findall() to CSV in addition to all input data 【发布时间】:2019-06-30 11:18:25 【问题描述】:

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

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


    如何将输入 CSV 的全部内容复制到输出 CSV 并将 postcode / found 正则表达式添加到找到它的行?

    是否有任何明显形式的错误检查或我遗漏的其他内容?

    是否存在更好的方法来将输入 CSV 的标头自动添加到输出 CSV 而无需明确指定它们?

    是否可以使用 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 文件提供示例数据将有助于回答问题。 【参考方案1】:

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

(.*),(.*),\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,并在每行的单独列中附加邮政编码。 正则表达式看起来像英国或加拿大(或者我猜测其他当前或前英联邦成员国随机)邮政编码的正则表达式。在显然没有它们的数据上强制使用仅限美国的邮政编码似乎是帝国主义的。 (询问我们是否尝试将我们的地址写入美国网站上的订单!)【参考方案2】:

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

    读取csv:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html 提取邮编:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.str.extract.html 写入csv:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_csv.html

【讨论】:

这对以后的参考很有用。仍然希望发现是否存在仅使用 csv 和 re 的解决方案。【参考方案3】:

您的示例中的 CSV 无效;看起来您缺少在地址字段周围的引用。

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

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

除此之外,这里是一个重构,它在每一行的末尾添加一个字段,并在其中嵌入一个以分号分隔的邮政编码列表(或者根本没有,如果正则表达式匹配不成功)字段。

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])

【讨论】:

以上是关于除了所有输入数据之外,将 re.findall() 输出到 CSV的主要内容,如果未能解决你的问题,请参考以下文章

re.match re.search re.findall区别

Python re.findall 打印所有模式

python)使用正则表达式查找所有匹配项(从 re.search 更改为 re.findall)[重复]

re模块

re(正则表达式)模块

网络爬虫re模块的findall()函数