无聊的背景故事:
我的银行业务报告有.pdf和.csv格式,但较旧的报告只能以pdf格式提供给我。我希望以与新版本相同的格式保存数据,以便于搜索,但pdf受到保护,在摆弄了解锁器和pdf-to-text转换器之后,我最终得到了格式化的文件。
我有文件,其中每一行都是一个事务(如:23.12 22.12.09 Verfügung Geldautomat\t63050000 / 9000481400\tGA NR00002317 BLZ63050000 0\t22.12/14.17UHR ESELSBERGW EUR 50,00\t-50,00
)
用数字替换数据字段会产生以下顺序和分隔符:
1 2 3\t7 / 6\t5\t4\t8
但是我希望它采用这种格式(原始数据中不存在的字段0,9和10是静态的)
"0";"1";"3";"4";"5";"6";"7";"8";"9";"10"
这是我目前的做法(没有I / O部分)
def readtrans(line):
d1, d2, rest = line.split(' ', 2)
d3, rest, d5, d4, d8 = rest.split('\t')
d7, d6 = rest.split(' / ')
return [d1, d2, d3, d4, d5, d6, d7, d8]
不幸的是它在第一个文件的第3行崩溃,因为字段5的某些值的字段5和6为空。在添加if子句以解决此问题之后,脚本前进到第5行只会再次崩溃,因为字段4也可能包含Tabs。我也可以解决这个问题,但我把它作为寻找更灵活解决方案的提示。
大多数情况下,当我需要从文本中提取数据时,我会相应地查看分隔符和split()
。它可能不是非常有效,但它比查找我很少使用并反复忘记的正则表达式语法更快。在这种情况下它是一种可行的方法还是正则表达式更适合?正则表达式甚至可以处理这个任务,如果是,它仍然可读吗?你会如何解决它?
编辑: 它是真的,我永远不会再使用这个代码,(顺便说一下,这是我的解决方案),但这是一个非常常见的问题
def readtrans(line):
d1, d2, rest = line.split(' ', 2)
if rest[0] == 'A':
d3, d7, d4, d8 = rest.split('\t')
d5 = ''
d6 = ''
else:
d3, d7d6, d5, d4d8 = rest.split('\t', 3)
d7, d6 = d7d6.split(' / ')
rest = d4d8.split('\t')
d8 = rest[-1]
d4 = ' '.join(rest[:-1])
return [d1, d2, d3, d4, d5, d6, d7, d8]
在思考如何重新解释我的问题之后,我意识到它基本上是这个问题的重复Convert string to variables (like format(), but in reverse)
有了新知识,我制作了这个简短的模式,正确地解析了我的例子
import re
example = '23.12 22.12.09 Verfügung Geldautomat\t63050000 / 9000481400\tGA NR00002317 BLZ63050000 0\t22.12/14.17UHR ESELSBERGW EUR 50,00\t-50,00'
x = re.search(r'(\S+) (\S+) ([\S| ]+)\t(\S+) / (\S+)\t([\S| ]+)\t([\S| ]+)\t([\S| ]+)', example)
print x.groups()
>>>('23.12',
'22.12.09',
'Verf\xc3\xbcgung Geldautomat',
'63050000',
'9000481400',
'GA NR00002317 BLZ63050000 0',
'22.12/14.17UHR ESELSBERGW EUR 50,00',
'-50,00')
关键是使用re.groups()
答案 0 :(得分:1)
我要做几个假设: 1)您可能永远不会再使用此代码 2)只有几种可能的格式
我不打算为此制定RE,因为它不需要如此强大。 (见假设1)。
我可能会尝试找出一些方法来确定我正在阅读的特定行使用的格式。然后使用一些if语句,通过适当的分隔步骤将其发送到您想要的顺序中获取字段。 (见假设2)。
我很快想出了一个例子,你显然需要改变一下才能使它适用于你的情况,但你明白了。最难的部分可能是找出一种方法来确定使用哪个解码器...我在我的案例中使用了“标签的位置”。
def decoder1(line):
parts = line.split("\t")
d1, d2 = parts[0].split(",")
d3, d4, d5, d6, d7, d8, d9 = parts[1].split(",")
return [d1, d2, d3, d4, d5, d6, d7, d8, d9]
def decoder2(line):
parts = line.split("\t")
d1 = parts[0]
d2, d3, d4, d5, d6, d7, d8, d9 = parts[1].split(",")
return [d1, d2, d3, d4, d5, d6, d7, d8, d9]
def decoder3(line):
parts = line.split("\t")
d1, d2, d3, d4, d5, d6, d7 = parts[0].split(",")
d8, d9 = parts[1].split(",")
return [d1, d2, d3, d4, d5, d6, d7, d8, d9]
if __name__ =="__main__":
lines = [
"1,2\t3,4,5,6,7,8,9",
"1\t2,3,4,5,6,7,8,9",
"1,2,3,4,5,6,7\t8,9"
]
for line in lines:
tablocation = len((line.split("\t")[0]).split(","))
if tablocation == 2:
res = decoder1(line)
elif tablocation == 1:
res = decoder2(line)
elif tablocation == 7:
res = decoder3(line)
else:
print "Must be a new format for %s" %line
res = "NA"
print res
如果您有多个'解码器选项',那么可能值得花时间开发一些RE。但是,如果不了解您所拥有的所有可能的变化,那么提供的帮助将比我在上述方法中所显示的更多。
答案 1 :(得分:1)
你的问题中有一些噪音,但这就是我所想的你想要的:
如何指定要拆分的多个分隔符,其中一些可能是分隔符 不止一个角色?
答案是使用re.split()
:
s = '1 2 3\t7 / 6\t5\t4\t8'
import re
re.split(r'\s/\s|\s|\t',s)
Out[13]: ['1', '2', '3', '7', '6', '5', '4', '8']
您可以根据需要将订单重新排列到最终输出中。
注意:通常在这些多分隔符问题中,您可以任意指定要分割的标记的顺序。在这里不是这样。
re.split(r'\s|\t|\s/\s',s)
Out[14]: ['1', '2', '3', '7', '/', '6', '5', '4', '8']
您需要在之前查找\s/\s
,只查找\s
,因为后者是前者的子字符串。