【问题标题】:Python 2.7 Match Exact String in the Row of a CSV FilePython 2.7 匹配 CSV 文件行中的精确字符串
【发布时间】:2017-06-22 14:15:22
【问题描述】:

我有一个 csv 文件,其中包含特定列中的字符串和其他列中的其他值。我还有一个字符串列表。 循环遍历行,我想检查列表中的字符串之一是否完全包含在 csv 文件的行中。 如果是,则此行必须写入新的 csv 文件。

csv 文件是一个行列表,例如:

22/06/2017 04:00:32 | string1 | value1   
22/06/2017 04:00:32 | string11 | value2    
22/06/2017 04:00:32 | string2 | value3   
22/06/2017 04:00:32 | string3 | value4

我写了这段代码,它工作正常,但它没有考虑字符串的“精确”匹配。

import os, csv

def filter_csv(folderpath):

     list1 = [
     ('name1',1,'string1','value1'),
     ('name2',2,'string2','value2'),
     ('name3',3,'string3','value3'),
     ('name4',4,'string4','value4'),
     ...
     ]

     def column(matrix, i):
         return [row[i] for row in matrix]

     col = column(list1,2)

     for file in os.listdir("%s" % folderpath):
         if file.endswith(".csv"):
             new_file = 'new_'+file
             filepath = os.path.join("%s" % folderpath, file)
             new_filepath = os.path.join("%s" % folderpath, new_file)

             with open('%s' % filepath) as csvfile:
                 lines = csvfile.readlines()

             with open('%s' % new_filepath, 'wb') as csvfile2:
                 for line in lines:
                     for namevar in col:
                         if namevar in line:
                             csvfile2.write(line)

     return

如何为 csv 文件的一列添加精确匹配的字符串?

【问题讨论】:

  • "exact" 匹配我的意思是:如果在 csv 中有“string111”,它会写入新的 csv 文件,因为“string1”包含在“string111”中,但我不希望这样,只有当第一个 csv 文件中有“string1”时,我才想写入新的 csv 文件。
  • csv 文件是喜欢的行列表:22/06/2017 04:00:32 |字符串1 |值1

标签: python string python-2.7 match


【解决方案1】:

您要做的是将过滤后的列表写入文件。

过滤条件是“某些预定义的有效值之一必须出现在 CSV 行上”,我们可以为此使用集合交集。我们需要一组有效值,当您将它们与当前输入行相交并保留一些时,当前输入行就会通过。

import os, csv
from glob import glob

def filter_csv(folderpath):

    list1 = [
        ('name1',1,'string1','value1'),
        ('name2',2,'string2','value2'),
        ('name3',3,'string3','value3'),
        ('name4',4,'string4','value4'),
        # ...
    ]

    # prepare a set of valid values
    valid_values = {row[2] for row in list1}

    for filepath in glob("%s/*.csv" % folderpath):
        filename = os.path.basename(filepath)
        new_filepath = os.path.join(folderpath, 'new_' + filename)

        with open(filepath, newline='') as infile, \
            open(new_filepath, 'wb') as outfile:

            reader = csv.reader(infile)
            writer = csv.writer(outfile)
            filtered_rows = (row for row in reader if valid_values.intersection(row))

            writer.writerows(filtered_rows)

注意事项

  • glob() 对于按扩展名查找文件非常有用
  • with 可以处理多个资源
  • 您可以使用\ 跨越多行
  • 集合已针对查找匹配值进行了优化
  • { ... } 是一个集合理解——它将一个列表变成一个集合
  • 始终使用 CSV 模块解析 CSV 文件 - 切勿使用 .split() 或类似的东西

【讨论】:

  • 不行,我猜glob()的行有错误。
  • *sigh*,in 关键字丢失了。
  • @bbastu 在代码中,OP 显示输入文件中的所有列都与某个列表进行比较,而不仅仅是一个列表。至少我是这么解释的。只比较一列比较简单,不需要.intersection() 调用。
  • 谢谢你,但它不匹配任何东西:-(
  • 我向您展示了如何原则上。我的意思是你要理解代码背后的意图。找出它不匹配任何东西的原因并修复它。这段代码中没有太多的活动部分,所以应该不会花很长时间。
【解决方案2】:

只是为了使用不同的库提供一个稍微不同的解决方案,现在我认为这可能有点矫枉过正,但您可能会喜欢它:) 您应该能够修改示例以插入到您的代码中...

import pandas as pd

# Dummy col
col = ["string1", "string2", "string3"]

# Read in CSV file
df = pd.read_csv("test.csv", header=None, skipinitialspace=True, delimiter ="|")

# Strip all strings so that trailing whitespace is ignored. 
# csv library OP used would also be "fooled" by whitepace
df_obj = df.select_dtypes(['object'])
df[df_obj.columns] = df_obj.apply(lambda x: x.str.strip())

# Select only rows with any column that has a value in col
df = df[df.isin(col).any(axis=1)]

# Write out CSV to new file
df.to_csv("test2.csv")

这将通过检查每一列是否有 col 中的一个值来过滤 CSV 的每一行。如果任何列在col 中有值,它就会出现在输出 CSV 文件中。

我发现的一件事是,如果 CSV 中的文本有尾随空格,则完全匹配将不起作用。例如,在string1 下方的 CSV 行中,由于尾随空格,不会完全匹配。

 value1, value2, string1   , value3

因此需要额外的代码来修剪所有字符串。使用csv 库进行了测试,它会有同样的问题。如果您知道您的 CSV 字符串永远不会有尾随空格,那么您甚至可以删除这两行。然后过滤的代码,完整的例子,将是(借用Tomalak's use of glob):

import pandas as pd
import glob
import os

def filter_csv(folderpath):

    list1 = [
        ('name1',1,'string1','value1'),
        ('name2',2,'string2','value2'),
        ('name3',3,'string3','value3'),
        ('name4',4,'string4','value4')
    ]

    def column(matrix, i):
        return [row[i] for row in matrix]

    col = column(list1,2)

    for filepath in glob.glob("%s/*.csv" % folderpath):
        filename = os.path.basename(filepath)
        new_filepath = os.path.join(folderpath, 'new_' + filename)
        df = pd.read_csv(filename, header=None, skipinitialspace=True, delimiter ="|")
        df_obj = df.select_dtypes(['object'])
        df[df_obj.columns] = df_obj.apply(lambda x: x.str.strip())
        df[df.isin(col).any(axis=1)].to_csv(new_filepath, sep="|", header=False, index=False)

但如果空格不是问题,您可以从代码中删除以下几行:

 df_obj = df.select_dtypes(['object'])
 df[df_obj.columns] = df_obj.apply(lambda x: x.str.strip())  

【讨论】:

  • 太棒了!这行得通!只需添加参数 sep=';'在该行中: df = pd.read_csv(filename, sep=';' , header=None, skipinitialspace=True)
猜你喜欢
  • 2016-03-08
  • 2017-10-30
  • 2020-10-01
  • 2021-10-18
  • 1970-01-01
相关资源
最近更新 更多