有没有更符合Python风格的方法来组合CSV元素?

3

基本上我使用Python定时任务从网页中读取数据,并将其以CSV列表的形式放置:

.....
###1309482902.37
entry1,36,257.21,16.15,16.168
entry2,4,103.97,16.36,16.499
entry3,2,114.83,16.1,16.3
entry4,130.69,15.6737,16.7498
entry5,5.20,14.4,17
$$$
###1309482902.37
entry1,36,257.21,16.15,16.168
entry2,4,103.97,16.36,16.499
entry3,2,114.83,16.1,16.3
entry4,130.69,15.6737,16.7498
entry5,5.20,14.4,17
$$$

我的代码基本上是做一个正则表达式搜索,然后遍历###和$$$之间的所有匹配项,然后逐行处理每个匹配项,将每一行按逗号分割。正如您所看到的,有些条目有4个逗号,而有些有5个。这是因为我很傻,没有意识到网络源在其4位数字中放置了逗号。例如:

entry1,36,257.21,16.15,16.168

实际上应该是

entry1,36257.21,16.15,16.168

我已经收集了大量数据,不想重写代码,所以我想到了一个麻烦的解决方法。是否有更Pythonic的方法来解决这个问题?

===

contents = ifp.read()

#Pull all entries from the market data
for entry in re.finditer("###(.*\n)*?\$\$\$",contents):

    dataSet = contents[entry.start():entry.end()]
    dataSet = dataSet.split('\n');

    timeStamp = dataSet[0][3:]
    print timeStamp

    for i in xrange(1,8):
        splits = dataSet[i].split(',')
        if(len(splits) == 5):
            remove = splits[1]
            splits[2] = splits[1] + splits[2]
            splits.remove(splits[1])
        print splits
        ## DO SOME USEFUL WORK WITH THE DATA ##

===


2
Pythonic 的方式应该是一开始就使用 csv运行 - Ignacio Vazquez-Abrams
4个回答

2

我会使用Python的csv模块来读取CSV文件,在遇到损坏的行时进行修复,然后使用csv.writer将CSV重新写出。像这样(假设您的原始文件中逗号放错了位置,文件名为ugly.csv,新的清理过的输出文件将是pretty.csv):

import csv

inputCsv = csv.reader(open("ugly.csv", "rb"))
outputCsv = csv.writer(open("pretty.csv", "wb"))

for row in inputCsv:
  if len(row) >= 5:
    row[1] = row[1] + row[2] #note that csv entries are strings, so this is string concatenation, not addition
    del row[2]
  outputCsv.writerow(row)

简洁明了,而且由于您使用了正确的CSV解析器和编写器,您不必担心引入任何新的奇怪情况(如果您在第一次脚本中使用它来解析网络结果,则输入数据中的逗号将被转义)。


0
通常使用csv模块来处理各种格式的CSV文件。
然而,在这里,存在一个逗号分隔的丑陋情况,因此需要一种丑陋的解决方案。我没有看到一个干净的解决方案,所以我认为使用任何有效的方法都是可以的。
顺便说一句,这一行似乎是多余的:
remove = splits[1]

你完全可以在这里使用 csv(而不是像正则表达式这样的丑陋方法)。读取CSV文件,在遇到损坏的行时进行修复,并使用 csv.writer 将CSV文件写回。 - JonathonW
我使用正则表达式是因为它不是真正的CSV。文件的形式为

... $$$每个数据集都受到此格式的限制,每个数据集包含CSV数据行。如果不是真正的CSV,那么使用正则表达式有什么问题呢?分割应该就足够了。
- Chris Anderson

0

其他人建议您使用csv来解析文件,这是个好建议。但它并没有直接解决另一个问题——即,您正在处理的是由数据段落组成的文件。通过将文件读入单个字符串,然后使用正则表达式解析该大字符串,您正在放弃文件的关键点杠杆。一种不同的策略是编写一个可以解析文件并逐段生成的方法。

def read_next_section(f):
    for line in f:
        line = line.strip()
        if line.startswith('#'):
            # Start of a new section.
            ts = line[3:]
            data = []
        elif line.startswith('$'):
            # End of a section.
            yield ts, data
        else:
            # Probably a good idea to use csv, as others recommend.
            # Also, write a method to deal with extra-comma problem.
            fields = line.split(',')
            data.append(fields)

with open(sys.argv[1]) as input_file:
    for time_stamp, section in read_next_section(input_file):
        # Do stuff.

非常好的干净代码。您能解释一下什么是杠杆作用吗?您是指由于我不会一次性读取整个文件而导致的更少的内存使用,还是由于较小的搜索空间而导致的更有效的搜索? - Chris Anderson
@Chris Anderson 我的观点不是关于内存或速度。相反,它涉及到解析策略。解析通常导致丑陋的代码。为了避免这种丑陋,您需要利用数据提供的关键杠杆点。这些杠杆点使您能够区分文件中有意义的部分。在您的情况下,有意义的单元是行和节。通过将文件吞入单个字符串,您会融化这些区别,因此必须跳过各种障碍来恢复它们(例如,在新行上拆分文本)。 - FMc

0

更符合 Python 风格的代码块写法

for i in xrange(1,8):
    splits = dataSet[i].split(',')
    if(len(splits) == 5):
        remove = splits[1]
        splits[2] = splits[1] + splits[2]
        splits.remove(splits[1])
    print splits

会是

for row in dataSet:
    name, data = row.split(',', 1)
    print [name] + data.rsplit(',', 2)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接