【问题标题】:Multiple lines into single line in PythonPython中的多行变成单行
【发布时间】:2025-12-06 20:30:02
【问题描述】:

我有一个文件如下:

>abc
AAA
AAA
>dfgg
BBBBB
BBBBB
BB
>zzz
CCCCC
CCC

我想要的输出是:

>abc
AAAAAA
>dfgg
BBBBBBBBBBBB
>zzz
CCCCCCCC

就是把多行变成单行。

我写了以下代码:

f = open('test.txt', 'r')
currentline = ""
for line in f:
    if line.startswith('>'):
        line = line.rstrip('\n')
        print line
    else:
        line = line.rstrip('\n')
        currentline = currentline + line
        print currentline
f.close()

当然,这是不对的,因为currentline 会一直增长到最后。我不知道如何更新currentline 并按指示打印输出。

我知道一种选择是使用f.read()f.readlines() 读取整个文件,并将文件视为字符串或列表,但由于文件非常大,并且不以'>'开头的每一行都可以最多2000万个字符,我认为最好不要一次将整个文件读入内存并逐行处理。请让我知道您对此有何看法。

感谢您的帮助!

【问题讨论】:

    标签: python


    【解决方案1】:

    一个简单的解决方案:

    from itertools import groupby
    
    with open('data.txt') as f:
        for key, group in groupby(f, lambda s: s.startswith('>')):
            print(''.join(s.rstrip('\n') for s in group))
    

    这仅在以> 开头的行都是单行时才有效,它们在您的示例中。为避免将它们连接起来,您可以执行以下操作:

    from itertools import groupby, count
    
    counter = count()
    with open('data.txt') as f:
        for key, group in groupby(f, lambda s: next(counter) if s.startswith('>') else -1):
            print(''.join(s.rstrip('\n') for s in group))
    

    关键是groupby 的键函数:count() 是一个生成器,它简单地生成整数序列 0、1、2。这意味着每个 > 行都有自己的唯一键,而所有其他行获得-1 的键并组合在一起,除非> 行介入。

    事实上,任何保持组唯一性的表达式都可以使用,它不必是计数器。例如你可以使用这个:

    lambda s: object() if s.startswith('>') else None
    

    文件迭代和groupby 都是惰性的,因此组将在读取组后的行后立即输出。

    【讨论】:

    • 我喜欢它,虽然不容易阅读。这真的会逐行处理和输出吗?你能解释一下 count() 在那里做什么吗?
    • @ypnos 我已经添加了更多解释并更正了计数器的错误(尽管错误并不重要,因为代码仍然在那里工作!)
    • 谢谢!您的解决方案的唯一缺点是它不会逐行打印,而是逐组打印。
    【解决方案2】:

    一进来就打印所有内容的版本:

    with open('test.txt', 'r') as f:
        flush = False
        for line in f:
            if line.startswith('>'):
                if flush:
                    print('')
                print(line.rstrip('\n'))
                flush = False
            else:
                flush = True
                print(line.rstrip('\n'), end='')
        if flush:
            print('')
    

    【讨论】:

    • 这看起来像 Python 3,其中 OP 显然使用 Python2
    • flushFalse 开头,可以变为True,但永远不会设置回False
    • @DSM:谢谢!当有多行以 '>' 开头时,这是相关的。
    【解决方案3】:

    您的代码很好,您只需找到合适的位置更新currentline。您将在找到下一个标志后进行更新,在您的情况下,该标志是以> 开头的行。

    f = open('test.txt', 'r')
    currentline = ""
    for line in f:
        if line.startswith('>'):
            line = line.rstrip('\n')
            if currentline != "": print currentline
            print line
            currentline = ""
        else:
            line = line.rstrip('\n')
            currentline = currentline + line
    print currentline
    f.close()
    
    
    Input:
    
    >abc
    AAA
    AAA
    >dfgg
    BBBBB
    BBBBB
    BB
    >zzz
    CCCCC
    CCC
    
    Output:
    
    >abc
    AAAAAA
    >dfgg
    BBBBBBBBBBBB
    >zzz
    CCCCCCCC
    
    # edited code above and tested it with the below file based on ypnos's comment.
    Input:
    
    >abc
    AAA
    AAA
    >dfgg
    BBBBB
    BBBBB
    BB
    >
    >
    >>
    >zzz
    CCCCC
    CCC
    
    Output:
    
    >abc
    AAAAAA
    >dfgg
    BBBBBBBBBBBB
    >
    >
    >>
    >zzz
    CCCCCCCC
    

    编辑:ypnos 提出了一个很好的观点,即上面会打印不必要的换行符。我对上面的代码做了一个小改动,它现在避免了打印这些代码。请参阅上面的新测试用例。

    【讨论】:

    • 这几乎和我之前发布的解决方案一样好。但在某些情况下,您会打印不必要的换行符。
    • 我相信唯一会出现的新行只是打印开头的一个新行
    • 如果你有连续的以'>'开头的行,你也可以有额外的换行符。
    • 好点,我修改了我的代码以避免这种情况。它仍然会打印带有'>'的行,但不会打印不必要的新行
    • 我投了反对票,因为它是我已经在我的答案中得到的更糟糕的版本,在我进一步改进我的答案之前。当输入为空时,它仍然会打印一个不必要的换行符,并且它会不必要地累积行(这就是我改进答案的原因)。因此,总的来说,它可能会起作用,但比其他几个答案更糟糕,并且在解决方案的工作方式上没有添加任何独特之处。
    【解决方案4】:

    试试这个

    f = open('test.txt', 'r')
    for line in f:
        if line.startswith('>'):
            print '\n', line.rstrip('\n')
        else:
            print line.rstrip('\n'), 
    f.close()
    

    【讨论】:

    • 哈哈,更简单了。但是你不会在开头产生一个不应该存在的换行符吗?最后错过换行符?
    • 连接后的行中有空格字符。我不熟悉在打印行末尾加逗号,您能解释一下吗?
    • 这是一个在 python2 中有效的技巧,但在 python3 中无效,以抑制 '\n' 在最后打印。正如你所说,这个解决方案在开头有额外的空格和换行符,最后缺少换行符。
    • 对于 py3 使用 print(line.rstrip('\n'), end='')