-1

I want to replace a specific string in a file by a variable's value:

In my file there is these 2 lines:

<CtrlSum>10</CtrlSum>
<sum>45</sum>

I want to search for the value "10" and replace it by "45".

I tried something like that:

with open(path,'r+') as f:
    lst=f.readlines()
    for j in lst:
        if'sum' in j:
            Somme = j.split('>')[1].split('<')[0]
            print(Somme)

    for i in lst:
        if 'CtrlSum' in i:
            Ctrl = i.split('>')[1].split('<')[0]
    print (Ctrl)

    f.writelines(f.replace(Ctrl,Somme))

But I have this error:

f.writelines(f.replace(Ctrl,Somme)) AttributeError: '_io.TextIOWrapper' object has no attribute 'replace'

What is wrong and how can I fix it?

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
  • `f` is a file. You can only call `.replace()` on a string (i.e. the result of `.read()`) – mousetail Sep 15 '20 at 12:30
  • Usually I create another file, copy the modified content to it and then rename it to override the original – geckos Sep 15 '20 at 12:30
  • what are you trying to do? replace what string with what? - what are you planning to achieve with these splits? – DevCl9 Sep 15 '20 at 12:31
  • i want to replace the value between < > on "CtrlSum" by value of "Sum" @DevCl9 – marcoAndrew Sep 15 '20 at 12:33
  • Are you asking how to parse an XML or HTML file? – mkrieger1 Sep 15 '20 at 12:49
  • its an XML file i tried with elementTree but cant make it worked so im just working with it like its a simple file – marcoAndrew Sep 15 '20 at 12:51
  • Well, the answer to the actual question you have asked is "you can't use the `replace` method with a file object, you have to use it with a string", but that won't solve your problem. – mkrieger1 Sep 15 '20 at 12:56
  • This should answer your question: https://stackoverflow.com/questions/6523886/find-and-replace-values-in-xml-using-python. If not, you should show a [mre] of input files, what exactly you have tried and what the problem was. – mkrieger1 Sep 15 '20 at 12:58

4 Answers4

1

You can simply do something like this:

import re

with open('a.txt','r') as f:
    text_value = f.read()

    sum_val = sum(map(int, re.findall(r'<sum>(\d+)</sum>', text_value)))

with open('a.txt','w') as f:
    f.write(re.sub(r'<CtrlSum>(.+)</CtrlSum>', f'<CtrlSum>{sum_val}</CtrlSum>', text_value))

No need for readlines() or writelines() or any loop. Just read the whole text with .read() -> process it -> write().

DevCl9
  • 298
  • 1
  • 9
  • Yeah but without readlines() i cant loop trough my files to seek different occurences of what i am searching for : in this case value between <> after "CtrlSum" String @DevCl9 – marcoAndrew Sep 15 '20 at 12:43
  • WDYM? could you share a sample input file and the desired output? (if this code doesn't cut it) – DevCl9 Sep 15 '20 at 12:47
  • imagine if in my file there is these 2 lines : 10 45 i want to search for the value "10" and replace it by "45" , sorry if its not very clear ! @DevCl9 – marcoAndrew Sep 15 '20 at 12:48
  • Yeah ! but that append all the text after the first one , is there a way to overwrite it ? – marcoAndrew Sep 15 '20 at 13:04
  • Yeah but if there is multiple occurence of "Sum" is there any way to add them ?@DevCl9 – marcoAndrew Sep 15 '20 at 13:44
  • You specifically said "`In my file there is these 2 lines:`" - anyway, what do you wish to happen if there are several such tags? replace with the nearest value? – DevCl9 Sep 15 '20 at 13:53
  • Yes im sorry , no just add the sum of " sum " values and replace " ctrl " by this sum ! @DevCl9 – marcoAndrew Sep 15 '20 at 14:09
  • @marcoAndrew updated! and please upvote/accept if you find it useful. thanks – DevCl9 Sep 15 '20 at 14:27
0

The replace() function can only be used on a string. Your f isn't a string, it's the file itself. You need to call f.read() to get the file content as a string and call replace() on this.

EDIT: Check this link, it explains different methods to write on files :)

https://pythonexamples.org/python-replace-string-in-file/

Armaaj
  • 33
  • 5
0

Use XML parsing

import xml.etree.ElementTree as ET

xml = '''<r><CtrlSum>10</CtrlSum>
<sum>45</sum></r>'''

root = ET.fromstring(xml)
root.find('.//CtrlSum').text = root.find('.//sum').text
ET.dump(root)

output

<r>
   <CtrlSum>45</CtrlSum>
   <sum>45</sum>
</r>
balderman
  • 22,927
  • 7
  • 34
  • 52
0

XSLT 3.0 solution:

<xsl:transform version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:mode on-no-match="shallow-copy"/>
 <xsl:template match="CtrlSum/text()">
   <xsl:value-of select="//sum"/>
 </xsl:template>
</xsl:transform>

(Using XSLT 1.0 is also possible, just a bit more verbose).

Michael Kay
  • 156,231
  • 11
  • 92
  • 164