使用Python将文本文件转换为CSV

3 投票
2 回答
3028 浏览
提问于 2025-04-17 06:25

我需要帮助来解析一个非常长的文本文件,内容大概是这样的:

NAME         IMP4   
DESCRIPTION  small nucleolar ribonucleoprotein 
CLASS        Genetic Information Processing
             Translation
             Ribosome biogenesis in eukaryotes
DBLINKS      NCBI-GI: 15529982
             NCBI-GeneID: 92856
             OMIM: 612981
///
NAME         COMMD9
DESCRIPTION  COMM domain containing 9
ORGANISM     H.sapiens
DBLINKS      NCBI-GI: 156416007
             NCBI-GeneID: 29099
             OMIM: 612299
///
.....

我想要得到一个结构化的csv文件,每一行的列数都要一样,这样我才能方便地提取我需要的信息。

我最开始是这样尝试的:

for line in a:
    if '///' not in line:
        b.write(''.join(line.replace('\n', '\t')))
    else:
    b.write('\n')

结果得到了一个这样的csv文件:

NAME         IMP4\tDESCRIPTION  small nucleolar ribonucleoprotein\tCLASS        Genetic Information Processing\t             Translation\t             Ribosome biogenesis in eukaryotes\tDBLINKS      NCBI-GI: 15529982\t            NCBI-GeneID: 92856\t
         OMIM: 612981
NAME         COMMD9\tDESCRIPTION  COMM domain containing 9\tORGANISM     H.sapiens\tDBLINKS      NCBI-GI: 156416007\t             NCBI-GeneID: 29099t\             OMIM: 612299

主要的问题在于,像DBLINKS这样的字段在原始文件中是分成多行的,这样就导致它们被拆分成了多个字段,而我希望它们能合并成一个字段。此外,并不是每一行都有所有字段,比如在这个例子中,'CLASS'和'ORGANISM'字段就没有出现在每一行。

我希望得到的文件应该是这样的:

NAME         IMP4\tDESCRIPTION  small nucleolar ribonucleoprotein\tNA\tCLASS        Genetic Information Processing; Translation; Ribosome biogenesis in eukaryotes\tDBLINKS      NCBI-GI: 15529982; NCBI-GeneID: 92856; OMIM: 612981
NAME         COMMD9\tDESCRIPTION  COMM domain containing 9\tORGANISM     H.sapiens\tNA\tDBLINKS      NCBI-GI: 156416007; NCBI-GeneID: 29099; OMIM: 612299

你能帮我一下吗?

2 个回答

0

这个脚本可以把你的文本文件转换成一个有效的CSV文件(比如可以用Excel打开的那种):

import sys
from sets import Set

if len(sys.argv) < 2:
    print 'Usage: %s <input-file> <output-file>' % sys.argv[0]
    sys.exit(1)

entries = []
entry = {}

# Read the input file
with open(sys.argv[1]) as input:
    lines = input.readlines()

for line in lines:
    # Check for beginning of new entry
    if line.strip() == '///':
        if len(entry) > 0:
            entries.append(entry)
        entry = {}
        continue

    # Check for presense of key
    possible_key = line[:13].strip()
    if possible_key != '':
        key = possible_key
        entry[key] = []

    # Assemble the value
    if key:
        entry[key].append(line[13:].strip())

# Append the last entry
if len(entry) > 0:
    entries.append(entry)

# 'entries' now contains a list of a dict of a list

# Find out all possible keys
all_keys = Set()
for entry in entries:
    all_keys.union_update(entry.keys())

# Write all entries to the output file
with open(sys.argv[2], 'w') as output:
    # The first line will contain the keys
    output.write(','.join(['"%s"' % key for key in sorted(all_keys)]))
    output.write('\r\n')

    # Write each entry
    for entry in entries:
       output.write(','.join(['"%s"' % ';'.join(entry[key]) if key in entry else '' for key in sorted(all_keys)]))
       output.write('\r\n')
5

你可以使用 itertools.groupby,第一次用来把多行合并成记录,第二次用来把多行字段收集成一个迭代器:

import csv
import itertools

def is_end_of_record(line):
    return line.startswith('///')

class FieldClassifier(object):
    def __init__(self):
        self.field=''
    def __call__(self,row):
        if not row[0].isspace():
            self.field=row.split(' ',1)[0]
        return self.field

fields='NAME DESCRIPTION ORGANISM CLASS DBLINKS'.split()
with open('data','r') as f:
    for end_of_record, lines in itertools.groupby(f,is_end_of_record):
        if not end_of_record:
            classifier=FieldClassifier()
            record={}
            for fieldname, row in itertools.groupby(lines,classifier):
                record[fieldname]='; '.join(r.strip() for r in row)
            print('\t'.join(record.get(fieldname,'NA') for fieldname in fields))

这样会产生

NAME         IMP4   DESCRIPTION  small nucleolar ribonucleoprotein  NA  CLASS        Genetic Information Processing; Translation; Ribosome biogenesis in eukaryotes DBLINKS      NCBI-GI: 15529982; NCBI-GeneID: 92856; OMIM: 612981
NAME         COMMD9 DESCRIPTION  COMM domain containing 9   ORGANISM     H.sapiens  NA  DBLINKS      NCBI-GI: 156416007; NCBI-GeneID: 29099; OMIM: 612299

上面的内容就是你看到的输出结果。它和你提供的期望输出是一样的,前提是你展示的是这个输出的 repr 形式。


使用到的工具参考:

撰写回答