如何在Python中覆盖文件?

7 投票
5 回答
59700 浏览
提问于 2025-04-17 19:36

我正在尝试覆盖一个文件。我是根据这个 在Python中读取和覆盖文件 的内容来写的。

为了完成我的代码:

<select class="select compact expandable-list check-list" 
    ONCHANGE="location = this.options[this.selectedIndex].value;">
    <option value="{% url envelopes:auto_sort %}?sort_by=custom">
        Custom order
    </option>
    <optgroup label="Category">
        <option value="{% url envelopes:auto_sort %}?sort_by=cat_asc">
            Ascending order
        </option>
        <option value="{% url envelopes:auto_sort %}?sort_by=cat_desc">
            Descending order
        </option>
    </optgroup>
</select>

def auto_sort(request):
    sort_by = request.GET.get('sort_by', None)
    if sort_by:
        temp_path = "{0}/file.txt".format(settings.SITE_ROOT) 

        f=open(temp_path,'r+')
        text = f.read()
        text = re.sub('cat_asc', 'cat_desc', text)
        f.seek(0)
        f.write(text)
        f.truncate()
        f.close();

        handle=open(temp_path,'w+')
        handle.write(sort_by)
        handle.close();

    return HttpResponseRedirect(reverse('envelopes:editor'))

我目前代码的输出结果:

文件里原本有 cat_desc,当我再次尝试写入 custom 时,结果变成了 customc。注意最后的 c,它应该只显示 custom

这是我想要实现的目标:

  1. 我在文件中写入,比如 cat_desc
  2. 如果我想再写一次,比如 custom,那么 cat_desc 必须被删除,并替换成 custom

5 个回答

5

新的回答...

你把 text 当作 re.sub 的第四个参数传入了。这个地方应该是一个 int 类型的数字。

Help on function sub in module re:

sub(pattern, repl, string, count=0, flags=0)
    Return the string obtained by replacing the leftmost
    non-overlapping occurrences of the pattern in string by the
    replacement repl.  repl can be either a string or a callable;
    if a string, backslash escapes in it are processed.  If it is
    a callable, it's passed the match object and must return
    a replacement string to be used.

旧的回答...

也许你在做的事情是

from os import open

那是一个不同(更底层)的打开方式,你只需要使用内置的 open 函数(使用它不需要导入任何东西)

这里有一个错误的例子,能让你看到错误信息

>>> from os import open
>>> open("some_path", "r+")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: an integer is required

另外,如果你想覆盖文件,需要用 "w+" 来打开。 "r" 是用来读取的。

5

关于你的新问题:

想要直接在文件里覆盖内容几乎是不可能的,除非你用新的字节串替换掉长度完全相同的旧字节串。比如说,如果你把 'cat_desc' 替换成 'cat_asc',最后的结果可能会变成 'cat_ascc'

你现在做的事情是:用 'r+' 模式打开文件,读取整个内容,处理后再用 seek 回到开头,最后写回去,这个方法是可行的。但这并不是最好的做法。

而且,无论如何,你的问题在于,刚做完这些后,你又用 'w+' 模式打开了同一个文件路径(这会清空文件),然后写入了不同的内容。所以,你刚写的内容就没了。

解决这个问题的方法就是……别这么做。我不太清楚你想要实现什么,但这可能不是你想要的结果。

同时,重写文件的最佳方法是“原子性写入临时文件并重命名”的方式。这种方法可以确保你不会损坏文件,你要么得到新文件,要么保留旧文件。这样也意味着你不需要把整个文件都放在内存中,可以逐步处理。而且这个方法非常简单……如果你不在乎Windows的话。它的工作方式是这样的:

with tempfile.NamedTemporaryFile(delete=False) as outfile:
    with open(inpath) as infile:
        # copy from infile to outfile, changing things as you go
    os.rename(outfile.name, inpath)

不幸的是,在Windows上实现这个方法非常麻烦。你不能在 outfile 还打开的时候移动它,也不能在 with 语句外访问它,此外,你还不能直接用 outfile 替换 infile;你必须进行复杂的操作。而且,除非你愿意使用Vista/2008并直接调用Win32 API,否则这个过程永远不会完全原子性。

4

根据你修改后的问题,或许下面这样的写法会更简单一些。

def auto_sort(request):
    sort_by = request.GET.get('sort_by', None)
    if sort_by:
        temp_path = "{0}/file.txt".format(settings.SITE_ROOT) 
        #Set new_text to whatever you want based on your logic
        new_text = 'custom' 
        f=open(temp_path,'w')
        f.write(new_text)
        f.close();

        handle=open(temp_path,'w+')
        handle.write(sort_by)
        handle.close();

    return HttpResponseRedirect(reverse('envelopes:editor'))

撰写回答