2017-10-07 111 views
0

我有一组带有;分隔符的.csv文件。我需要用空白替换数据中的某些垃圾值。样本问题行:使用Python查找和替换问题

103273;CAN D MAT;B.C.;B.C.;B.C.;03-Apr-2006

所需的行之后查找和替换是:

103273;CAN D MAT;;;;03-Apr-2006

在我与;;

,我不能没有更换;B.C.;上面的例子只需要B.C.,因为我需要匹配此特定错误情况下的整个单元格值。我使用的代码是:

import os, fnmatch 

def findReplace(directory, filePattern): 
     for path, dirs, files in os.walk(os.path.abspath(directory)): 
      for filename in fnmatch.filter(files, filePattern): 
       filepath = os.path.join(path, filename) 
       with open(filepath) as f: 
        s = f.read() 
       for [find, replace] in zip([';#DIV/0!;',';B.C.;'],[';;',';;']   
        s = s.replace(find, replace) 
       with open(filepath, "w") as f: 
        f.write(s) 

findReplace(*Path*, "*.csv") 

说我不是得到的输出是:

103273;CAN D MAT;;B.C.;;03-Apr-2006

可有人请这个问题的帮助?

在此先感谢!

+0

所以基本上你想用''(空字符串)替换'#DIV/0!'和'B.C.'。为什么不这样做呢?用直接的方法。 – nutmeg64

+0

发布的程序将给出''103273; CAN D MAT ;;;;;;;; 03-Apr-2006''作为示例输入,这与您写的内容不同。 – janos

+0

@nutmeg:我也有短语B.C. (作为单元格中字符串的一部分)。我只想替换整个单元格值与此匹配的位置。而且,这两个值只是代表性的。我还有大约20个其他的值,比如“January”和“January”。 另外,我是新来的python所以不能确定你的意思是直接的方法。 –

回答

2

[find, replacement]对不适合您的目的。 替换; +值+ ;;;实际上只是一种复杂的方式,表示您要删除value的列。的

因此,而不是使用[find, replacement]对, 它会更自然和直接对;分割线领域, 替换被视为垃圾与空字符串, 值,然后再加入值:

JUNK = frozenset(['#DIV/0!', 'B.C.']) 

def clean(s): 
    return ';'.join(map(lambda x: '' if x in JUNK else x, s.split(';'))) 

您可以在您的实现使用此功能(或复制其在线):

def findReplace(directory, filePattern): 
    for path, dirs, files in os.walk(os.path.abspath(directory)): 
     for filename in fnmatch.filter(files, filePattern): 
      filepath = os.path.join(path, filename) 

      cleaned_lines = [] 
      with open(filepath) as f: 
       for line in f.read(): 
        cleaned_lines.append(clean(line)) 

      with open(filepath, "w") as f: 
       f.write('\n'.join(cleaned_lines)) 
+0

更正了错字。 我不太了解它是如何工作的,但从我所了解的情况来看,该计划正在捡起第一个和最后一个; B.C。作为替换的字符串而忽略中间的字符串。 –

+0

@SagarJoshi哦,我明白了。我重写了我的答案。 – janos

+0

@SagarJoshi你需要更多的帮助吗? – janos

1

str.replace,一旦^ h作为一个替代品,在最后一件物品被替换后继续从下一个字符开始扫描。所以当两个;B.C.;重叠时,它不会取代两者。

您可以使用the re module更换B.C.仅当出现两份;之间,利用先行和后向断言:

>>> import re 
>>> s = "103273;CAN D MAT;B.C.;B.C.;B.C.;03-Apr-2006" 
>>> re.sub(r'(?<=;)B[.]C[.](?=;)', "", s) 
'103273;CAN D MAT;;;;03-Apr-2006' 

...但是,在这种情况下,它可能是更好的上线分裂成田;,替换与要擦除的字符串匹配的字段,然后再次将字符串连接在一起。

>>> fields = s.split(';') 
>>> for i, f in enumerate(fields): 
...  if f in ('B.C.', '#DIV/0!'): 
...   fields[i] = '' 
... 
>>> ';'.join(fields) 
'103273;CAN D MAT;;;;03-Apr-2006' 

这有两个主要的优点:你不必为每个被替换的字符串写一个相当复杂的正则表达式;如果其中一个字段位于行首或行尾,它仍然可以工作。

对于任何CSV分析比这更复杂(例如,如果任何字段可以包含引用的;字符,或者如果文件具有应该跳过的标题),请查看csv module

+0

我会试试这个。我不太确定加入弦乐部分。数据有点混乱,它包含逗号和分号作为单元值的一部分。 (该文件是csv,具有分号分隔符,但该字符串也包含这些字符) –

+0

@SagarJoshi如果引用文字分号,例如在数据中出现为'a \; b'或'“a; b”',那么你应该使用'csv'模块来解析它。如果这不起作用,正则表达式可能是最好的选择(尽管不一定像我在这里所做的那样)。 – trentcl

+0

(可能值得明确指出'csv'支持许多方言,比如'''''''''''限制和各种引用形式[方言](https://docs.python.org/2/library/csv.html #方言和格式化参数)) – trentcl