如何将文本文件的多行作为字典中键的值(在元组中)?

2024-05-28 19:42:56 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图读取一个文本文件,然后使用该文件中的数据创建一个字典。文本的一个例子是:

100
Mulan
300, 500
200, 400

200
Ariel
100, 500
500

300
Jasmine
500
500, 100

400
Elsa
100, 500


500
Belle
200, 300
100, 200, 300, 400

我需要将第一行作为字典的键,第二行、第三行和第四行作为该键的值。在

到目前为止,我的代码如下:

^{pr2}$

但这会返回一些奇怪的东西:

{'100': [], 'Mulan': [], '300,': ['500'], '200,': ['300'], '200': [], 'Ariel': [], '100,': ['200,', '300,', '400'], '500': [], '300': [], 'Jasmine': [], '500,': ['100'], '400': [], 'Elsa': [], 'Belle': []}

显然,这个函数将每行的第一个值作为键,这不是我想要的。我需要输出如下所示:

{100: ('Mulan', [300, 500], [200, 400]), 
200: ('Ariel', [100, 500], [500]), 
300: ('Jasmine', [500], [500, 100]), 
400: ('Elsa', [100, 500], []), 
500: ('Belle', [200, 300], [100, 200, 300, 400])}

有谁能帮我弄清楚如何实现这一点,或者具体地说,如何从文本文件中提取多行作为字典中的值?在


Tags: 文件数据函数代码文本字典elsa例子
3条回答

这应该是一种更短、更直观的方法:

import itertools

file_name = 'sample.txt'
d = {}
expected_lines = 4

with open(file_name, 'r') as f:
    blocks = list("".join(group) for empty, group in itertools.groupby(f, key=str.isspace) if not empty)

for block in blocks:
    lines = [[int(i) for i in i.split(',')] if len(i.split(',')) > 1 
                                            else i for i in block.split('\n')][:expected_lines]
    lines = [[] if i == '' else i for i in lines]
    d[lines[0]] = tuple(lines[1:])

d
{'100': ('Mulan', [300, 500], [200, 400]),
 '200': ('Ariel', [100, 500], '500'),
 '300': ('Jasmine', '500', [500, 100]),
 '400': ('Elsa', [100, 500], []),
 '500': ('Belle', [200, 300], [100, 200, 300, 400])}

当您在这里打开文件时,我们使用with上下文管理器,并使用Python的标准模块库itertools.groupby将文件拆分为空换行符分隔的块。然后对于每个块,使用第一个项作为字典中的键,并将每个后续项放入元组中。它还以列表形式返回整数对,如果不满足示例中预期的行数,则返回空列表。在


另一个需要考虑的有趣场景是,当键和值之间缺少行时,上面的解决方案将无法提供正确的输出,例如:

^{pr2}$

对于这个场景,我们希望数据是5块,包括换行符。然后我们可以利用Python配方here中的grouper函数,一次从文件中抓取5行。我们也可以使用helper函数显式地从整数对中返回数据。在

import itertools

file_name = 'sample.txt'
d = {}
expected_lines = 5

def grouper(iterable, n, fillvalue=''):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.zip_longest(*args, fillvalue=fillvalue)

def check_empty(int_pairs):
    int_pairs = int_pairs.split()
    if len(int_pairs) > 1:
        return int_pairs
    else:
        return []

with open(file_name, 'r') as f:
    blocks = list(grouper(f, expected_lines, fillvalue=''))

for block in blocks:
    lines = [i.replace('\n','') for i in block if i][:expected_lines-1]
    d[int(lines[0])] = (lines[1],check_empty(lines[2]),check_empty(lines[3]))

这将适用于缺少行的文件,其中行号指示数据块或结构。在

一个类似于Maik-Kahnt的解决方案,它还包括Elsa的空列表,并且还依赖于每个记录有5行。在

from pprint import pprint

fin = open('f2.txt', 'r')

array = []
dict_ = {}

lines = fin.readlines()

for i, line in enumerate(lines):
    line = line.rstrip()
    if i % 5 == 0:
        key = int(line)
    elif i % 5 == 1:
        name = line
    elif i % 5 == 2:
        if len(line):
            rec = [int(j) for j in line.split(', ')]
        else:
            rec = []
        array.append(rec)
    elif i % 5 == 3:
        if len(line):
            rec = [int(j) for j in line.split(', ')]
        else:
            rec = []
        array.append(rec)
        dict_[key] = (name, *array)
        array = []

pprint(dict_)

输出为:

^{pr2}$

选项1

看看你的例子,名字后面必须有两个列表。如果第二个是空的,你希望那里有一个空列表。因此,您已经在数据中强制使用了“5行定义字典条目”的结构。。阅读时不妨使用:

from pprint import pprint

with open('data.txt', 'r') as F:
    lines = [line.replace('\n','') for line in F.readlines()]


n = len(lines)
d = 5                   # number of lines for one entry in the file

if not n%d==0:
    for i in range(d-n%d):
        lines.append('')    

result = {}
for i, line in enumerate(lines):
    if   i%5==0: key  = int(line)
    elif i%5==1: name = line.rstrip()
    elif i%5==2: 
        if line=='': num1 = []
        else: num1 = [int(x) for x in line.replace(' ','').split(',')]
    elif i%5==3:
        if line=='': num2 = []
        else: num2 = [int(x) for x in line.replace(' ','').split(',')]  
    elif i%5==4: result[key] = (name, num1, num2)

pprint(result)

结果就是你想要的。在

^{pr2}$

“if not n%d==0:”部分添加空行,直到有一个5的倍数的总和。这样,“belle”条目会被添加,甚至很难在您的数据文件中只有24行。在

选项2

如果您不需要空列表,可以从这里开始:

with open('data.txt', 'r') as F:
    lines = F.readlines()


long_line = ''.join([x.replace(' ','') for x in lines])
split     = [x.lstrip().split('\n') for x in long_line.split('\n\n')]


result    = {}
for e in split:
    result[int(e[0])] = (e[1], e[2:])


for key in sorted(result.keys()):
    print(key, result[key])

输出:

100 ('Mulan', ['300,500', '200,400'])
200 ('Ariel', ['100,500', '500'])
300 ('Jasmine', ['500', '500,100'])
400 ('Elsa', ['100,500'])
500 ('Belle', ['200,300'])

我知道这不是你的产出。但正如我所说:如果这些空名单并不重要,你可以从这里开始工作。在

相关问题 更多 >

    热门问题