简体   繁体   中英

Editing a SPECIFIC word in a file in python, every 20 mins

I want to change the value of omega in the file below at a specific interval, say every 30 minutes. This file is used by another program at runtime. I would like to ramp the RPM value (the last entry on the line) from 0-10 slowly. Essentially, the value "5" in the file below needs to change from 0-10 in increments of 1 every 30 minutes.

rotor
{
    // Fixed patches (by default they 'move' with the MRF zone)
    nonRotatingPatches (); //if something blows up ..fill the non rotating patches between "(baffles, pressureOutlet etc)"

    origin    origin [0 1 0 0 0 0 0]  (0 0 0);
    axis      axis   [0 0 0 0 0 0 0]  (0 0 1);
    omega    omega [0 0 -1 0 0 0 0] 5;
}

I have tried the following code (I don't know how to do the every 30 minutes part), but the data written by the file.writelines(data) method doesn't seem to incorporate the change I make to the value.

import os
import sys
import fileinput

with open('MRFzones', 'r') as file:
    data = file.readlines()

# The line omega is line 27 in the file.
line = data[26]
word = line.split()

# >>> data[26].split()
# ['omega', 'omega', '[0', '0', '-1', '0', '0', '0', '0]', '5;'] 
# The part i want to change is the 10th word.
word[9] = '10;'

# Write everything back.
with open('MRFzones', 'w') as file:
    file.writelines(data)

This is not working - the 5 is not being changed into 10 in the updated file. Can someone help me implement this? I might not even have the right idea, so we could start the code from scratch if necessary.

Thanks, Suresh

You are not modifying data at all -- you are extracting line and word and modify those. In the end, you write back the original data to the file.

I would not use Python for this, but rather a shell script (provided you are on a Unix-like OS):

for i in 0 1 2 3 4 5 6 7 8 9 10; do
   sleep 30
   sed -i "26s/[0-9]+;/$i/" MRFzones
od

You can identify your lines and its components via the fileinput module you imported, and the re module for regular expressions:

>>> import re
>>> pattern = r'(\s*omega\s+omega\s+\[-?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ -?\d+\]) (-?\d+);'

Enclosing braces are used to capture patterns, have a look at the re module to know more (very useful Python module, very well done and documented) !

We have our pattern, now browse our file inplace: print is used to write in the file directly (no longer prints to screen), standard function comes back to normal when the for loop is over (file closed).

Don't forget to remove newlines to avoid having unrequired newlines in your output file (yes this is a bit annoying: the line variable given by the loop contains a newline \n ) !

>>> from fileinput import FileInput
>>> path = 'D:\\Temp\\Toto.txt'
>>> for line in FileInput(path, inplace=1):
        line = line.replace('\n', '')
        match = re.match(pattern, line)
        if match:
            line = '{0} {1};'.format(match.groups()[0], '145')
            print line
        else:
            print line

You can now browse your old file, the value 5 was replaced by 145 . I personally don't agree with Sven, Python is for me the perfect language to deal with this kind of situation.

The canonical way of doing what you want is indeed to use the regular expression module ( re ). It can perform the replacement in the "omega" line directly:

import re

for count in range(10):

    # Reading:
    with open('MRFzones') as input_file:  # Automatically closes the file after reading
        contents = input_file.read()  # Whole file

    # Writing:
    with open('MRFzones', 'w') as out_file:
        # The 'omega.*(\d+)' searches for a line with "omega" followed at some point by digits and ";".  sub() performs
        # a substitution of the part matched inside the parentheses:
        out_file.write(re.sub('omega +(\d+);', str(count), contents, count=1))  # count=1 only modifies the first omega line

With Python, you could use a regex and processing in a file with mode 'rb+':

import re
from time import sleep

regx = re.compile('^ +omega +omega +\[[ \d.eE+-]+\] +(\d\d?)(;.+)',re.MULTILINE|re.DOTALL)

for i in xrange(1,11):
    sleep(20*60)
    with open('MRFzones', 'rb+') as f:
        m = regx.search(f.read())
        x = m.start(1)
        f.seek(x,0)
        f.writelines((str(i),m.group(2)))
        f.tuncate()

I suppose that the initial file is containing

'     omega    omega [0 0 -1 0 0 0 0] 0;'

m is the MatchObject that records the results of the matching Among these results, there are:

  • the position in the file's content where starts the 1 or 2 digit(s) sequence that is catched by the first group in the match, the one defined by (\d\d?) . This start is obtained via method start() of m called with 1 as argument
  • the text catched by second group in the match, defined by (;.+) , obtained by method group() of m called with 2 as argument

re.MULTILINE makes the symbol '^' in the pattern of the regex to match with each start of line, that is precisely after each '\n' and at the beginning of the string. Without re.MULTILINE , '^' means only the matching with the beginning of the string

re.DOTALL makes the dot in the pattern to match with any character, comprised the newlines. So ';.+' means: the character ';' and all and every characters that follow ';'until the very end of the string. Without re.DOTALL , the symbolic dot in the pattern would stop to match at the end of the line.

After the execution of f.read() , the file's pointer is situated at the end of the file on the hard drive. seek() allows to move it. In this case, seek(x,0) moves the pointer to a position that is situated at x characters from the start of the file (the 0 means 'from the start', see seek() to learn the other types of moves)

After the execution of f.seek(x,0) , the pointer is just before the integer you want to change. Writing str(i) erases and writes on the ancient number.

f.truncate() is to make the file ending at the current position. In the case of my code, it's not absolutely necesssary, since the string that is replaced is always replaced by a string of same or longer length.

.

Note that this code can be simplified if there are two positions reserved for the number in the file where can be written two characters: ' 0', ' 1', ' 2', ' 3', etc

In this case the code can be:

import re
from time import sleep

regx = re.compile('^ +omega +omega +\[[ \d.eE+-]+\] +(\d\d?)',re.MULTILINE)

for i in xrange(1,11):
    sleep(20*60)
    with open('MRFzones', 'rb+') as f:
        m = regx.search(f.read())
        x = m.start(1)
        f.seek(x,0)
        f.write('{:>2}'.format(i))

or

import re
from time import sleep

regx = re.compile('^ +omega +omega +\[[ \d.eE+-]+\] +(\d\d?)(;.+)',re.MULTILINE|re.DOTALL)

with open('Copie de oso.txt', 'rb+') as f:
    m = regx.search(f.read())
    x = m.start(1)
    f.seek(x,0)
    f.writelines((' 1',m.group(2)))
    f.truncate()

for i in xrange(2,11):
    sleep(20)
    with open('Copie de oso.txt', 'rb+') as f:
        f.seek(x,0)
        f.write('{:>2}'.format(i))

.

PS

Brackets '[' and ']' of the symbolic sequence [ \d.eE+-] in the pattern define a set of characters. I put between these two brackets all the characters that are likely to be used to write a number. It is important that '-' be at the end of the set, otherwise it would mean anaother thing that just the '-' character.

'\[' and '\]' are the escaped brackets, to represent just the characters 'brackets', not the symbolic brackets that define a set

I choosed to catch '[0 0 -1 0 0 0 0]' with the part \[[ \d.eE+-]+\] of the pattern in case that the numbers could be any other kind of number, expressed in any sort of representation (comprised exponential notation)

' +' in the pattern means 'any number of blanks'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM