0

I am trying to edit a file as follows in python 2.7.10 and running into below error, can anyone provide guidance on what the issue is on how to edit files?

import fileinput,re
filename = 'epivers.h'
text_to_search = re.compile("#define EPI_VERSION_STR         \"(\d+\.\d+) (TOB) (r(\d+) ASSRT)\"")
replacement_text = "#define EPI_VERSION_STR         \"9.130.27.50.1.2.3 (r749679 ASSRT)\""
with fileinput.FileInput(filename, inplace=True, backup='.bak') as file:
    for line in file:
        print(line.replace(text_to_search, replacement_text))
file.close()

Error:-

Traceback (most recent call last):
  File "pythonfiledit.py", line 5, in <module>
    with fileinput.FileInput(filename, inplace=True, backup='.bak') as file:
AttributeError: FileInput instance has no attribute '__exit__'

UPDATE:

import fileinput,re
import os
import shutil
import sys
import tempfile
filename = 'epivers.h'
text_to_search = re.compile("#define EPI_VERSION_STR         \"(\d+\.\d+) (TOB) (r(\d+) ASSRT)\"")
replacement_text = "#define EPI_VERSION_STR         \"9.130.27.50.1.2.3 (r749679 ASSRT)\""

with open(filename) as src, tempfile.NamedTemporaryFile(
        'w', dir=os.path.dirname(filename), delete=False) as dst:

    # Discard first line
    for line in src:
        if text_to_search.search(line):
            # Save the new first line
            line = text_to_search .sub(replacement_text,line)
            dst.write(line + '\n')
        dst.write(line)

# remove old version
os.unlink(filename)

# rename new version
os.rename(dst.name,filename)

I am trying to match line define EPI_VERSION_STR "9.130.27 (TOB) (r749679 ASSRT)"

Jeremyapple
  • 253
  • 2
  • 6
  • 20
  • It doesn't know how to close it. Try it without the `with`. – whackamadoodle3000 Mar 05 '18 at 01:02
  • it now throws error `Traceback (most recent call last): File "pythonfiledit.py", line 7, in print(line.replace(text_to_search, replacement_text)) TypeError: expected a character buffer object` – Jeremyapple Mar 05 '18 at 01:04
  • Why are you using `FileInput`? – Stephen Rauch Mar 05 '18 at 01:08
  • @StephenRauch - Do you have an alternative?I googled and found this one of the ways to edit a file – Jeremyapple Mar 05 '18 at 01:12
  • A basic `with open(filename) as file:` – Stephen Rauch Mar 05 '18 at 01:14
  • https://docs.python.org/2/tutorial/inputoutput.html#reading-and-writing-files – avigil Mar 05 '18 at 01:15
  • @StephenRauch - I want to do inlace editing so I was using `FileInput`,can you please be specific onhow this can be done using `with open(filename) as file:` – Jeremyapple Mar 05 '18 at 01:26
  • Go read: https://docs.python.org/2/library/fileinput.html#fileinput.FileInput Inplace is but a small convenience. It is not actually inplace.I would argue that this is making the problem more complicated than it needs to be, and I would not use it. See here for the way I would suggest: https://stackoverflow.com/a/48734140/7311767 – Stephen Rauch Mar 05 '18 at 01:33
  • @StephenRauch - in the example you suggested you are discarding the first line and writing a new one ` # Discard first line src.readline()`,in my case I want to match line in `text_to_search` and the replace it with `replacement_text` – Jeremyapple Mar 05 '18 at 01:46
  • For you, the example is about managing the creation of a new file, and then copying the result on top of the old file. The actual changes to the file in the example do not matter to you. – Stephen Rauch Mar 05 '18 at 01:47
  • @StephenRauch - I updated on what I tried with your suggestion but I see an empty file, where am I going wrong? – Jeremyapple Mar 05 '18 at 02:05
  • You are not looping through your file. You need to loop and write the things to your new files that it needs. You almost certainly do not want copyfileobj. – Stephen Rauch Mar 05 '18 at 02:08
  • @StephenRauch - Thanks,I added a loop now but I dont see the line edited, please suggest what am I still missing – Jeremyapple Mar 05 '18 at 02:16
  • Updated latest code – Jeremyapple Mar 05 '18 at 02:22
  • you are making this way more complicated then it has to be! `open(filename, 'r').read()` to get the contents, do the replacement, then `open(filename, 'w')` and write the edited text back. – avigil Mar 05 '18 at 02:58

2 Answers2

2

If r is a compiled regular expression and line is a line of text, the way to apply the regex is

r.match(line)

to find a match at the beginning of line, or

r.search(line)

to find a match anywhere. In your particular case, you simply need

line = r.sub(replacement, line)

though in addition, you'll need to add a backslash before the round parentheses in your regex in order to match them literally (except in a few places where you apparently put in grouping parentheses around the \d+ for no particular reason; maybe just take those out).

Your example input string contains three digits, and the replacement string contains six digits, so \d+\.\d+ will never match either of those. I'm guessing you want something like \d+(?:\.\d+)+ or perhaps very bluntly [\d.]+ if the periods can be adjacent.

Furthermore, a single backslash in a string will be interpreted by Python, before it gets passed to the regex engine. You'll want to use raw strings around regexes, nearly always. For improved legibility, perhaps also prefer single quotes or triple double quotes over regular double quotes, so you don't have to backslash the double quotes within the regex.

Finally, your usage of fileinput is wrong. You can't use it as a context manager. Just loop over the lines which fileinput.input() returns.

import fileinput, re

filename = 'epivers.h'
text_to_search = re.compile(r'#define EPI_VERSION_STR         "\d+(?:\.\d+)+ \(TOB\) \(r\d+ ASSRT\)"')
replacement_text = '#define EPI_VERSION_STR         "9.130.27.50.1.2.3 (r749679 ASSRT)"'
for line in fileinput.input(filename, inplace=True, backup='.bak'):
    print(text_to_search.sub(replacement_text, line))

In your first attempt, line.replace() was a good start, but it doesn't accept a regex argument (and of course, you don't close() a file you opened with with ...). In your second attempt, you are checking whether the line is identical to the regex, which of course it isn't (just like the string "two" isn't equivalent to the numeric constant 2).

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • why do you say `In your particular case, you don't actually seem to need a regex, though.,`, `I am trying to match line #define EPI_VERSION_STR "9.130.27 (TOB) (r749679 ASSRT)"` ,the `9.130.27` and `749679` can change – Jeremyapple Mar 05 '18 at 02:37
  • Just reviewed your question and realized the same thing. See updated answer already. – tripleee Mar 05 '18 at 02:38
  • I updated my code with your suggestion,still see the same issue,I dont see the line replaced – Jeremyapple Mar 05 '18 at 02:45
  • Your `fileinput` was wrong, too. See update again now. – tripleee Mar 05 '18 at 02:57
  • am not using fileinput anymore ,see my `UPDATE`,Stephen suggested its an overkill, do you suggest otherwise? – Jeremyapple Mar 05 '18 at 03:00
  • I actually kind of like it for simple things like this. Up to you really. Your regex was wrong so I have now updated that, too. – tripleee Mar 05 '18 at 03:02
  • Implemented one more minor regex fix; works for the test input `#define EPI_VERSION_STR "9.130.27 (TOB) (r123456 ASSRT)"` with nine spaces after `EPI_VERSION_STR` – tripleee Mar 05 '18 at 03:11
  • this throws error `Traceback (most recent call last): File "pythonfiledit.py", line 46, in text_to_search = re.compile(r'#define EPI_VERSION_STR "\d+\(?:.\d+)+ \(TOB\) \(r\d+ ASSRT\)"') File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py", line 194, in compile return _compile(pattern, flags) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py", line 251, in _compile raise error, v # invalid expression sre_constants.error: unbalanced parenthesis` – Jeremyapple Mar 05 '18 at 03:13
  • Then you are not really running the stock `re` module from Python 2.7. Try replacing `(?:` with just `(` then. – tripleee Mar 05 '18 at 03:16
  • regex doesn't match even after changing (?: to ( – Jeremyapple Mar 05 '18 at 03:31
  • I think I have done what I can to show you how to solve this. Your question doesn't contain a simple test case I can validate against and I'm done guessing. Try commenting out parts of the regex until you see where it's wrong. – tripleee Mar 05 '18 at 03:34
  • not able to match `9.130.27`,I already gave an example , `#define EPI_VERSION_STR "9.130.27 (TOB) (r749679 ASSRT)"` – Jeremyapple Mar 05 '18 at 03:38
  • 1
    open a separate question to fix your regex, you are convoluting separate problems with your code. The original question here has been answered. – avigil Mar 05 '18 at 03:40
  • I fixed the regex as `text_to_search = re.compile(r'#define EPI_VERSION_STR\s+"\d+\.\d+\.\d+\s+\(TOB\)\s+\(r\d+ ASSRT\)"') but it doesn't go inside the for loop and replace ` – Jeremyapple Mar 05 '18 at 03:51
  • Did this really work for you for the example I gave?even after fixing the regex it doesn't work,here is the link http://refiddle.com/refiddles/5a9cc41875622d34d70e0000 – Jeremyapple Mar 05 '18 at 04:10
  • I tested it, but just noticed that there was a copy/paste error in my transcript. Updated the answer once more. Demo: https://pastebin.com/e42nZ2di – tripleee Mar 05 '18 at 04:22
  • I dont see any update ...what was the copy/paste error? – Jeremyapple Mar 05 '18 at 04:34
  • `(?:\.` pro `\(?:.` -- the text "edited XX ago" next to my profile link is a link to the revisions, with diffs. – tripleee Mar 05 '18 at 04:35
  • @tripleee - I already fixed the regex, the issue now is even after fixing the regex it doesn't replace in my file... – Jeremyapple Mar 05 '18 at 04:41
  • I will figure out my regex issues, thanks a lot tripleee for helping so far – Jeremyapple Mar 05 '18 at 04:58
0

Read the file, use re.sub to substitute, then write the new contents back:

with open(filename) as f:
    text = f.read()

new_text = re.sub(r'#define EPI_VERSION_STR         "\d+\(?:.\d+\)+ \(TOB\) \(r\d+ ASSRT\)"',
                  '#define EPI_VERSION_STR         "9.130.27.50.1.2.3 (r749679 ASSRT)"',
                  text)

with open(filename, 'w') as f:
    f.write(new_text)
avigil
  • 2,218
  • 11
  • 18
  • There are no literal round parens around the first version number either. – tripleee Mar 05 '18 at 03:12
  • @avigil - this throws error `File "pythonfiledit.py", line 38, in text) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py", line 155, in sub return _compile(pattern, flags).sub(repl, string, count) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py", line 251, in _compile raise error, v # invalid expression sre_constants.error: unbalanced parenthesis ` – Jeremyapple Mar 05 '18 at 03:14
  • there was a missing slash to escape at `\(?:.\d+)+`, fixed. to be fair, these are regex issues. My solutions shows you how to edit a file but the regex and substitution to use are up to you. – avigil Mar 05 '18 at 03:20
  • Erm no, there should not be a backslash around these parentheses. If the OP is not using a standard regex library then up to them really to clarify what they use and how to specify a regex for it. – tripleee Mar 05 '18 at 03:33