【问题标题】:Correct way to write line to file?将行写入文件的正确方法?
【发布时间】:2011-09-03 19:58:18
【问题描述】:

我习惯了print >>f, "hi there"

但是,print >> 似乎已被弃用。上面这行的推荐方法是什么?

更新: 关于"\n" 的所有这些答案...这是通用的还是特定于 Unix 的? IE,我应该在 Windows 上做"\r\n" 吗?

【问题讨论】:

  • "\n" 不是 Unix 特定的。当文件以文本模式(默认)打开时,它会自动转换为当前平台的正确行尾。写“\r\n”会产生“\r\r\n”,这是错误的。
  • 只需添加 print ord(os.linesep) 语句即可查看 ascii 代码(大多数 UNIX 系统上为 10)
  • 为什么你认为它被弃用了?

标签: python file-io


【解决方案1】:

这应该很简单:

with open('somefile.txt', 'a') as the_file:
    the_file.write('Hello\n')

来自文档:

写入以文本模式打开的文件时不要使用os.linesep作为行终止符(默认);在所有平台上都使用单个 '\n'。

一些有用的阅读:

【讨论】:

  • 这个例子比打开/关闭例子要好。使用with 是记住关闭文件的一种更安全的方法。
  • 我不用打电话给the_file.close() ?
  • @HorseSMith:我明白了。希望我最近的编辑能澄清我的意图。如果我的答案“相当无用且具有误导性”,请随时编辑我的答案。
  • @user3226167:这是一个有趣的观点。但是你为什么要打开一个二进制文件来写纯文本呢?
【解决方案2】:

您应该使用自 Python 2.6+ 起可用的 print() 函数

from __future__ import print_function  # Only needed for Python 2
print("hi there", file=f)

对于 Python 3,您不需要 import,因为 print() 函数是默认值。

替代方法是使用:

f = open('myfile', 'w')
f.write('hi there\n')  # python will convert \n to os.linesep
f.close()  # you can omit in most cases as the destructor will call it

引用 Python documentation 关于换行符:

在输出时,如果换行符为 None,则写入的任何 '\n' 字符都将转换为系统默认的行分隔符 os.linesep。如果换行符是'',则不进行翻译。如果换行符是任何其他合法值,则写入的任何 '\n' 字符都将转换为给定字符串。

【讨论】:

  • -1 "如果你想确定,将 os.linesep 添加到字符串而不是 \n" 需要 newline="" 否则你会在 Windows 上得到 \r\r\n。完全没有理由对 os.linesep 大发雷霆。
  • @Sorin:您添加写入模式的编辑当然是一种改进。然而,奇怪的是,你对 os.linesep 仍然顽固不化。看我的回答。顺便说一句,您引用的文档适用于 3.x,但这部分在文本模式下也适用于 2.x:任何写入的 '\n' 字符都将转换为系统默认的行分隔符 os。 linesep* ... Windows:编写 os.linesep 与编写 \r\n 相同,其中包含 \n 被转换为 os.linesep 即 \r\n 所以最终结果是 \r\r\n。
  • @John 你是对的,我更正了 os.linesep 错误。谢谢。
  • 为了追加不是open('myfile','a') 而不是open('myfile','w')?
  • @BradRuderman 这是POSIX 标准的一部分,用于在文本文件中构成“行”,即文本文件中的每一行都必须以换行符终止,即使是最后一行。
【解决方案3】:

python docs推荐这种方式:

with open('file_to_write', 'w') as f:
    f.write('file contents\n')

所以这是我通常这样做的方式:)

来自docs.python.org的声明:

处理文件时最好使用 'with' 关键字 对象。这样做的好处是文件在之后正确关闭 它的套件完成了,即使在途中引发了异常。它是 也比编写等效的 try-finally 块要短得多。

【讨论】:

  • 当我需要将with 嵌套在循环中时,我不喜欢这种方式。这使我在循环中继续打开和关闭文件。也许我在这里遗漏了一些东西,或者这在这种特殊情况下真的是一个缺点?
  • 在with中循环怎么样?
  • @j7nn7k for line in fd:
【解决方案4】:

关于 os.linesep:

这是一个在 Windows 上完全未经编辑的 Python 2.7.1 解释器会话:

Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.linesep
'\r\n'
>>> f = open('myfile','w')
>>> f.write('hi there\n')
>>> f.write('hi there' + os.linesep) # same result as previous line ?????????
>>> f.close()
>>> open('myfile', 'rb').read()
'hi there\r\nhi there\r\r\n'
>>>

在 Windows 上:

正如预期的那样,os.linesep 确实不产生与'\n' 相同的结果。它不可能产生相同的结果。 'hi there' + os.linesep 等价于'hi there\r\n',NOT 等价于'hi there\n'。

就这么简单:使用\n,它会自动转换为 os.linesep。 自从 Python 首次移植到 Windows 以来,它就是这么简单。

在非 Windows 系统上使用 os.linesep 没有意义,而且在 Windows 上会产生错误的结果。

不要使用 os.linesep!

【讨论】:

  • 很好的例子——好奇你是不是 ipython 用户?格式化会话的好功能
  • 我不完全确定您想在这里告诉我们什么。 os.linesep 将返回操作系统定义的行术语字符(或字符串)。 Windows 默认使用 \r\n 作为行尾。但是,单个 \n 被识别。使用 \n 将提供完全可移植的 OUTPUT,但 os.linesep 在 Windows 上并没有错。
  • @Gusdor:关键是如果你在 Windows 中以文本模式显式使用os.linesep,结果是\r\r\n,这是错误的。 “Windows 使用...”毫无意义。 C 运行时库(以及 Python)在文本模式下的输出中将 \n 转换为 \r\n。其他软件的行为可能不同。在以文本模式阅读时,并非所有在 Windows 上运行的软件都将单独的 \n 识别为行分隔符。 Python 可以。微软的记事本文本编辑器没有。
  • 可能会有其他人阅读它,而不是你,使用一些米老鼠软件会吐出额外的\r ...
  • @Gusdor 你是从不同的语言来到 python 的吗,使用 '\n' 会导致在窗口上输出 '\n' 而不是 '\r\n' - 所以它缺乏愚蠢的文本编辑器期望的'\ r'?正如 John 所说,这不是 Python 的行为方式——'\n' 会自动替换为 '\r\n',如果这是 os.linesep 所说的那样。因此,在这里明确地说 os.linesep is “错误”。就像Department of Redundancy Department。是的,你可以做到。不,你不想。
【解决方案5】:

我认为没有“正确”的方式。

我会使用:

with open ('myfile', 'a') as f: f.write ('hi there\n')

纪念Tim Toady。

【讨论】:

  • 但是 OP 可能想要在文件中写入额外的内容。当with 超出范围时,文件将在此处关闭。
  • 呃,是的。这就是使用 with 的想法。如果要保持文件打开,只需在开头调用 open 并在完成后调用 close...
  • @mtrw。真的。 OP 正在追加。
  • 就 python 而言,RIP Tim Toady - 非常非常非常理所当然地
  • zen of perl 有点矛盾。
【解决方案6】:

在 Python 3 中它是一个函数,但在 Python 2 中,您可以将其添加到源文件的顶部:

from __future__ import print_function

那你就做吧

print("hi there", file=f)

【讨论】:

    【解决方案7】:

    如果您要写入大量数据并且担心速度,您可能应该选择f.write(...)。我做了一个快速的速度比较,在执行大量写入时它比print(..., file=f)快得多。

    import time    
    
    start = start = time.time()
    with open("test.txt", 'w') as f:
        for i in range(10000000):
            # print('This is a speed test', file=f)
            # f.write('This is a speed test\n')
    end = time.time()
    print(end - start)
    

    在我的机器上,write 平均在 2.45 秒内完成,而 print 大约需要 4 倍的时间(9.76 秒)。话虽如此,在大多数现实世界的场景中,这都不是问题。

    如果您选择使用print(..., file=f),您可能会发现您需要不时取消换行符,或者用其他内容替换它。这可以通过设置可选的end参数来完成,例如;

    with open("test", 'w') as f:
        print('Foo1,', file=f, end='')
        print('Foo2,', file=f, end='')
        print('Foo3', file=f)
    

    无论您选择哪种方式,我都建议您使用with,因为它使代码更易于阅读。

    更新:这种性能差异的原因是write 被高度缓冲并在实际写入磁盘之前返回(参见this answer),而print (可能)使用行缓冲。对此的一个简单测试是同时检查长写入的性能,其中行缓冲的缺点(在速度方面)不会那么明显。

    start = start = time.time()
    long_line = 'This is a speed test' * 100
    with open("test.txt", 'w') as f:
        for i in range(1000000):
            # print(long_line, file=f)
            # f.write(long_line + '\n')
    end = time.time()
    
    print(end - start, "s")
    

    现在性能差异变得不那么明显了,write 的平均时间为 2.20 秒,print 的平均时间为 3.10 秒。如果您需要连接一堆字符串来获得这种长线性能会受到影响,所以print 会更有效的用例有点少见。

    【讨论】:

    • 每行无需多次调用print(),您可以将每行作为位置参数输入,并使用print(line1, line2, ... sep="\n")指定换行符。这只会进行一次写入调用。
    【解决方案8】:

    从 3.5 开始,您还可以为此目的使用 pathlib:

    Path.write_text(data, encoding=None, errors=None)

    以文本模式打开指向的文件,写入数据,然后关闭文件:

    import pathlib
    
    pathlib.Path('textfile.txt').write_text('content')
    

    【讨论】:

      【解决方案9】:

      当您说 Line 时,它​​表示一些以 '\n' 字符结尾的序列化字符。行应该在某个点最后,所以我们应该在每行的末尾考虑 '\n'。这是解决方案:

      with open('YOURFILE.txt', 'a') as the_file:
          the_file.write("Hello")
      

      在每次写入后的追加模式下光标移动到新行,如果你想使用w模式你应该在write()函数的末尾添加\n字符:

      the_file.write("Hello\n")
      

      【讨论】:

      • "在每次写入后以追加模式移动光标到新行" – 不,不是。
      【解决方案10】:

      也可以使用io 模块,如下所示:

      import io
      my_string = "hi there"
      
      with io.open("output_file.txt", mode='w', encoding='utf-8') as f:
          f.write(my_string)
      

      【讨论】:

        【解决方案11】:

        如果您想避免使用write() 或writelines() 并自己用换行符连接字符串,您可以将所有行传递给print(),并将换行符分隔符和文件句柄作为关键字参数传递。此 sn-p 假定您的字符串没有尾随换行符。

        print(line1, line2, sep="\n", file=f)
        

        您不需要在末尾添加特殊的换行符,因为 print() 会为您完成。

        如果列表中有任意数量的行,您可以使用列表扩展将它们全部传递给print()。

        lines = ["The Quick Brown Fox", "Lorem Ipsum"]
        print(*lines, sep="\n", file=f)
        

        在 Windows 上可以使用"\n" 作为分隔符,因为print() 也会自动将其转换为 Windows CRLF 换行符 ("\r\n")。

        【讨论】:

          【解决方案12】:

          如果您想以每行的格式在列表中插入项目,可以这样开始:

          with open('somefile.txt', 'a') as the_file:
              for item in items:
                  the_file.write(f"{item}\n")
          

          【讨论】:

            【解决方案13】:

            要在flask中的文件中写入文本可以使用:

            filehandle = open("text.txt", "w")
            filebuffer = ["hi","welcome","yes yes welcome"]
            filehandle.writelines(filebuffer)
            filehandle.close()
            

            【讨论】:

            • 最好使用with open('file_to_write', 'w') as f: 语句写入文件。如果有人不小心在两者之间写了一些东西会导致没有明确的close() 调用,那么确保文件不会保持打开状态会容易得多
            【解决方案14】:

            你也可以试试filewriter

            pip install filewriter

            from filewriter import Writer
            
            Writer(filename='my_file', ext='txt') << ["row 1 hi there", "row 2"]
            

            写入my_file.txt

            采用支持 __str__ 的可迭代对象或对象。

            【讨论】:

              【解决方案15】:

              当我需要写很多新行时,我定义了一个使用 print 函数的 lambda:

              out = open(file_name, 'w')
              fwl = lambda *x, **y: print(*x, **y, file=out) # FileWriteLine
              fwl('Hi')
              

              这种方法的好处是它可以利用 print 函数提供的所有功能。

              更新:正如Georgy在评论部分提到的,可以通过partial函数进一步改进这个想法:

              from functools import partial
              fwl = partial(print, file=out)
              

              恕我直言,这是一种更实用且不那么神秘的方法。

              【讨论】:

              • 或者另一种(可能更简洁)的写法:from functools import partial; fwl = partial(print, file=out).
              • @Georgy 你的方法太好了,可以作为一个新的答案。
              • 思路和你的一样,只是实现有点不同。如果需要,您可以将其添加到答案的编辑中。我很好。
              【解决方案16】:

              既然别人已经回答了怎么做,那我就一行一行的回答。

              with FileOpenerCM('file.txt') as fp: # is equal to "with open('file.txt') as fp:"
                    fp.write('dummy text')
              

              这是一个所谓的context manager,任何带有with 块的东西都是上下文管理器。那么让我们看看这在幕后是如何发生的。

              class FileOpenerCM:
                   def __init__(self, file, mode='w'):
                       self.file = open(file, mode)
                    
                   def __enter__(self):
                        return self.file
                    
                   def __exit__(self, exc_type, exc_value, exc_traceback):
                       self.file.close()
              

              第一个方法__init__是(众所周知)对象的初始化方法。每当创建对象时,肯定会调用 obj.__init__。这就是你放置所有初始化代码的地方。

              第二种方法__enter__有点意思。你们中的一些人可能没有看到它,因为它是上下文管理器的特定方法。它返回的是在as 关键字之后分配给变量的值。在我们的例子中,fp。

              最后一个方法是在捕获错误或代码退出 with 块后运行的方法。 exc_type、exc_value、exc_traceback 变量是保存发生在 with 块内部的错误值的变量。例如,

              exc_type: TypeError
              exc_value: unsupported operand type(s) for +: 'int' and 'str
              exc_traceback: <traceback object at 0x6af8ee10bc4d>
              

              从前两个变量中,您可以获得有关错误的足够信息。老实说,我不知道第三个变量的用途,但对我来说,前两个就足够了。如果您想对上下文管理器进行更多研究,那么您肯定可以做到,并注意编写类并不是编写上下文管理器的唯一方法。使用contextlib,您也可以通过函数(实际上是生成器)编写上下文管理器。完全由你来看看它。你一定可以试试 带有 contextlib 的生成器函数,但我看到类更干净。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2015-07-02
                • 2015-08-30
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-10-21
                • 1970-01-01
                相关资源
                最近更新 更多