81

有没有办法做到这一点?假设我有一个文件是这样的名称列表:

  1. 阿尔弗雷德
  2. 账单
  3. 唐纳德

我如何在第 x 行(在本例中为 3)插入第三个名字“查理”,并自动将所有其他人发送到一行?我见过其他类似的问题,但他们没有得到有用的答案。可以做到吗,最好用方法或循环?

4

10 回答 10

110

这是一种方法。

with open("path_to_file", "r") as f:
    contents = f.readlines()

contents.insert(index, value)

with open("path_to_file", "w") as f:
    contents = "".join(contents)
    f.write(contents)

indexvalue是您选择的行和值,行从 0 开始。

于 2012-05-08T22:10:20.563 回答
29

如果您想在文件中搜索子字符串并将新文本添加到下一行,其中一种优雅的方法如下:

import fileinput
for line in fileinput.FileInput(file_path,inplace=1):
    if "TEXT_TO_SEARCH" in line:
        line=line.replace(line,line+"NEW_TEXT")
    print line,
于 2015-10-20T14:38:38.187 回答
10

我发现有一种技术组合对解决这个问题很有用:

with open(file, 'r+') as fd:
    contents = fd.readlines()
    contents.insert(index, new_string)  # new_string should end in a newline
    fd.seek(0)  # readlines consumes the iterator, so we need to start over
    fd.writelines(contents)  # No need to truncate as we are increasing filesize

在我们的特定应用程序中,我们想在某个字符串之后添加它:

with open(file, 'r+') as fd:
    contents = fd.readlines()
    if match_string in contents[-1]:  # Handle last line to prevent IndexError
        contents.append(insert_string)
    else:
        for index, line in enumerate(contents):
            if match_string in line and insert_string not in contents[index + 1]:
                contents.insert(index + 1, insert_string)
                break
    fd.seek(0)
    fd.writelines(contents)

如果您希望它在匹配的每个实例之后插入字符串,而不仅仅是第一个,请删除else:(并且正确地取消缩进)和break.

另请注意,and insert_string not in contents[index + 1]:防止它在 之后添加多个副本match_string,因此重复运行是安全的。

于 2017-06-02T22:55:43.437 回答
7

您可以将数据读入列表并将新记录插入您想要的位置。

names = []
with open('names.txt', 'r+') as fd:
    for line in fd:
        names.append(line.split(' ')[-1].strip())

    names.insert(2, "Charlie") # element 2 will be 3. in your list
    fd.seek(0)
    fd.truncate()

    for i in xrange(len(names)):
        fd.write("%d. %s\n" %(i + 1, names[i]))
于 2012-05-08T22:13:34.597 回答
5

接受的答案必须将整个文件加载到内存中,这对于大文件来说效果不佳。以下解决方案将插入右行的新数据的文件内容写入同一目录中的临时文件(因此在同一文件系统上),一次只从源文件中读取小块。然后它以一种有效的方式(Python 3.8+)用临时文件的内容覆盖源文件。

from pathlib import Path
from shutil import copyfile
from tempfile import NamedTemporaryFile

sourcefile = Path("/path/to/source").resolve()
insert_lineno = 152  # The line to insert the new data into.
insert_data = "..."  # Some string to insert.

with sourcefile.open(mode="r") as source:
    destination = NamedTemporaryFile(mode="w", dir=str(sourcefile.parent))
    lineno = 1

    while lineno < insert_lineno:
        destination.file.write(source.readline())
        lineno += 1

    # Insert the new data.
    destination.file.write(insert_data)

    # Write the rest in chunks.
    while True:
        data = source.read(1024)
        if not data:
            break
        destination.file.write(data)

# Finish writing data.
destination.flush()
# Overwrite the original file's contents with that of the temporary file.
# This uses a memory-optimised copy operation starting from Python 3.8.
copyfile(destination.name, str(sourcefile))
# Delete the temporary file.
destination.close()

编辑 2020-09-08:我刚刚在 Code Review 上找到了一个答案,该答案与上面类似,但有更多解释——它可能对某些人有用。

于 2020-06-13T22:13:03.423 回答
4
  1. file.readlines()使用或将文件解析为 python 列表file.read().split('\n')
  2. 根据您的标准,确定必须插入新行的位置。
  3. 使用 插入一个新的列表元素list.insert()
  4. 将结果写入文件。
于 2012-05-08T22:10:10.357 回答
4

您没有向我们展示输出应该是什么样子,因此一种可能的解释是您希望将其作为输出:

  1. 阿尔弗雷德
  2. 账单
  3. 查理
  4. 唐纳德

(插入 Charlie,然后将 1 添加到所有后续行。)这是一种可能的解决方案:

def insert_line(input_stream, pos, new_name, output_stream):
  inserted = False
  for line in input_stream:
    number, name = parse_line(line)
    if number == pos:
      print >> output_stream, format_line(number, new_name)
      inserted = True
    print >> output_stream, format_line(number if not inserted else (number + 1), name)

def parse_line(line):
  number_str, name = line.strip().split()
  return (get_number(number_str), name)

def get_number(number_str):
  return int(number_str.split('.')[0])

def format_line(number, name):
  return add_dot(number) + ' ' + name

def add_dot(number):
  return str(number) + '.'

input_stream = open('input.txt', 'r')
output_stream = open('output.txt', 'w')

insert_line(input_stream, 3, 'Charlie', output_stream)

input_stream.close()
output_stream.close()
于 2012-05-09T01:27:50.790 回答
2

一个简单但效率不高的方法是读取整个内容,更改它然后重写它:

line_index = 3
lines = None
with open('file.txt', 'r') as file_handler:
    lines = file_handler.readlines()

lines.insert(line_index, 'Charlie')

with open('file.txt', 'w') as file_handler:
    file_handler.writelines(lines)
于 2019-02-05T12:55:05.603 回答
2
location_of_line = 0
with open(filename, 'r') as file_you_want_to_read:
     #readlines in file and put in a list
     contents = file_you_want_to_read.readlines()

     #find location of what line you want to insert after
     for index, line in enumerate(contents):
            if line.startswith('whatever you are looking for')
                   location_of_line = index

#now you have a list of every line in that file
context.insert(location_of_line, "whatever you want to append to middle of file")
with open(filename, 'w') as file_to_write_to:
        file_to_write_to.writelines(contents)

这就是我最终获得要插入文件中间的任何数据的方式。

这只是伪代码,因为我很难清楚地了解正在发生的事情。

基本上你读入整个文件并将其添加到一个列表中,然后将你想要的行插入到该列表中,然后重新写入同一个文件。

我相信有更好的方法可以做到这一点,可能效率不高,但至少对我来说更有意义,我希望对其他人有意义。

于 2019-10-21T05:55:27.750 回答
-1

对于您自己创建原始文件并且碰巧知道插入位置的特殊情况,下面是一个稍微尴尬的解决方案(例如,您提前知道需要在第三行之前插入带有附加名称的行,但在您获取并写下其余名称之前不会知道名称)。我认为,按照其他答案中的描述,读取、存储然后重写文件的全部内容比这个选项更优雅,但对于大文件来说可能是不可取的。

您可以在插入位置留下一个不可见空字符 ('\0') 的缓冲区,以便稍后覆盖:

num_names = 1_000_000    # Enough data to make storing in a list unideal
max_len = 20             # The maximum allowed length of the inserted line
line_to_insert = 2       # The third line is at index 2 (0-based indexing)

with open(filename, 'w+') as file:
    for i in range(line_to_insert):
        name = get_name(i)                    # Returns 'Alfred' for i = 0, etc.
        file.write(F'{i + 1}. {name}\n')

    insert_position = file.tell()             # Position to jump back to for insertion
    file.write('\0' * max_len + '\n')         # Buffer will show up as a blank line

    for i in range(line_to_insert, num_names):
        name = get_name(i)
        file.write(F'{i + 2}. {name}\n')      # Line numbering now bumped up by 1.

# Later, once you have the name to insert...
with open(filename, 'r+') as file:            # Must use 'r+' to write to middle of file 
    file.seek(insert_position)                # Move stream to the insertion line
    name = get_bonus_name()                   # This lucky winner jumps up to 3rd place
    new_line = F'{line_to_insert + 1}. {name}'
    file.write(new_line[:max_len])            # Slice so you don't overwrite next line

不幸的是,没有办法删除任何没有被覆盖的多余空字符(或者通常是文件中间任何位置的任何字符),除非您随后重写所有内容。但是空字符不会影响您的文件对人类的外观(它们的宽度为零)。

于 2019-07-16T18:26:40.603 回答