Skip to content

Instantly share code, notes, and snippets.

@rqelibari
Last active December 23, 2017 16:18
Show Gist options
  • Save rqelibari/d2ab0edc56e25e5b1d86a782b26403d4 to your computer and use it in GitHub Desktop.
Save rqelibari/d2ab0edc56e25e5b1d86a782b26403d4 to your computer and use it in GitHub Desktop.
Python >2.7 function to replace a string in a file using mmap.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals,
with_statement)
import mmap
import os.path
def replace_placeholder_in_file(filepath, placeholder, text):
"""Replace `placeholder` in file `filename` with `text` if found.
Do nothing, if the file given by `filepath` does not exist. Replaces are
done in-place. If `text` is longer than `placeholder` the file is expanded.
If `text` is shorter the file is truncated by the amount of excessive bytes.
Otherwise there is just a replacement.
Examples:
>>> replace_placeholder_in_file("somefile.txt", "Hallo", "Hello")
Args:
string filepath - The path to the file, where the placeholder shall be
replaced.
string placeholder - The placeholder string, for which to look inside
the file given by `filepath`. This string will get
replaced by `text` if found inside the file.
string text - The replacement text.
"""
if not os.path.isfile(filepath):
return
placeholder_size = len(placeholder.encode("utf-8"))
text_size = len(text.encode("utf-8"))
new_bytes = text_size - placeholder_size
filesize_before_modification = os.path.getsize(filepath)
if new_bytes > 0:
# Pad file to right size
with open(filepath, 'a') as f:
f.write(new_bytes * b'\0')
with open(filepath, "r+b") as f:
mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_WRITE)
begin = mm.find(placeholder.encode("utf-8"))
if begin == -1:
# Nothing to replace here.
return
# mm.resize(filesize + NEW_BYTES) does not work on macOS
end_of_placeholder = begin + placeholder_size
following_bytes_length = filesize_before_modification -\
end_of_placeholder
mm.move(end_of_placeholder + new_bytes,
end_of_placeholder,
following_bytes_length)
mm.seek(begin)
mm.write(text.encode("utf-8"))
mm.flush()
mm.close()
if new_bytes < 0:
f.seek(new_bytes, os.SEEK_END)
f.truncate()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment