Adding text in the middle of the file - python

I have these files:
actions.js - append before }
import {constants} from "./constants";
export const setUser = (value) => ({
type: constants.SET_USER,
payload: value,
});
//here
constants.js - append to the end
export const constants = {
SET_USER: "SET_USER",
//here
};
reducers.js - add a const above export and inside the combineReducers object
import {constants} from "./constants";
import {combineReducers} from "redux";
const user = (state = null, action) => action.type === constants.SET_USER ? action.payload : state;
//here
export const reducers = combineReducers({
user,
// here
})
And I want to add code into these files in the places where I put //here. How can I do that with Python? I know I can write over a file with open('file', 'w').write('string') but how can I actually add text without loosing and overwriting the file? I want to add the text to the existing file, not to create the file, or overwrite it. I want it to have the old text, and add the new text to it. How can I achieve this with Python?
I made it append to the actions.js like this:
import sys
import os
reducer = sys.argv[1]
open("actions.js","a").write("""export const set{reducer} = (value) => ({{
type: constants.{constant},
payload: value,
}});
""".format(reducer=reducer.capitalize(), constant=constant))
But I have no idea how to get the others done

Read the file, slice the string at index you want, concatenate in order, and then write to the file with cursor at 0. Let x.txt be your file. "export" in the index() method here refers to a unique non repeating word. You can use unique comments to slice the string at respective positions!
with open("x.txt","r+") as f:
old=f.read()
print(old)
constant_text= "What you want to add??"
result=old[0:old.index("export")] + constant_text + old[old.index("export"):]
# print(result)
f.seek(0)
f.write(result)
print("######################################")
print(result)
Make sure the index keywords are unique if you want to slice in multiple locations using keywords!

To my knowledge, this is not possible in the way you suggest in a single operation. My solution of choice would be to iterate over the file’s lines, and once you hit your // here - marker, insert the code.
new_content = ""
with open(file_name) as f:
for line in f.readlines():
new_content += line
if line.strip() == "// here":
new_content += text_to_insert
After this loop, new_content should hold the old text and the new* inserted at the right place, which you can then write to any file you like.
*assuming that your input is properly formatted, including line breaks and so on.

Related

Adding new keys/values to nested dictionary in geojson file

I have a .js containing data used to plot points on a map. In the file, I have a single line var statement which contains nested dictionaries.
var json_Project = {"type":"FeatureCollection","name":"project","crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}},"features":[{"type":"Feature","properties":{"ID":"001","field1":"someText","field2":"someText"},"geometry":{"type":"Point","coordinates":[-1.14,60.15]}},{"type":"Feature","properties":{"ID":"002","field1":"someText","field2":"someText"},"geometry":{"type":"Point","coordinates":[-1.14,60.15]}}]}
One of these contains the "features" key which stores a list of dictionaries for each point (see below for readability).
var json_Project = {"type":"FeatureCollection","name":"project","crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}},"features":[
{"type":"Feature","properties":{"ID":"001","field1":"someText","field2":"someText"},"geometry":{"type":"Point","coordinates":[-1.14,60.15]}},
{"type":"Feature","properties":{"ID":"002","field1":"someText","field2":"someText"},"geometry":{"type":"Point","coordinates":[-1.14,60.15]}}]}
The "properties" for the first point has "ID":"001", second point has "ID":"001" etc.
I would like to insert a new property for each point with "INFORMATION:" and get the value from another dictionary using the keys from "ID".
info_dict = {'001': 'This is Project 1',
'002': 'This is Project 2'}
Specifically, I would like this new property to be inserted before "field2" so that it looks like:
var json_Project = {"type":"FeatureCollection","name":"project","crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}},"features":[
{"type":"Feature","properties":{"ID":"001","field1":"someText","INFORMATION":"This is Project 1","field2":"someText"},"geometry":{"type":"Point","coordinates":[-1.14,60.15]}},
{"type":"Feature","properties":{"ID":"002","field1":"someText","INFORMATION":"This is Project 2","field2":"someText"},"geometry":{"type":"Point","coordinates":[-1.14,60.15]}}]}
My futile attempt involved converting the one-liner to a string and then extract everything after the "features:" string as that is unique in the file. But not sure how to add the new key/value pair.
with open('project.js') as f:
contents = f.read().split()
contents_toString = ''.join(contents)
new_contents = re.findall('(?<="features":\[).*$', contents_toString)
EDIT
Thanks to #TenaciousB, I can read the geojson file and add in the "INFORMATION" property with the correct values from the dictionary:
with open('project.js') as f:
contents = f.read()
x = json.loads(contents)
for y in x['features']:
key = y['properties']['ID']
description = dictionary[key]
y['properties']['INFORMATION'] = description
I am still unsure how to:
Remove var json_Project = programmatically before being read;
Place the "INFORMATION" property before "field2";
Re-insert var json_Project = , save and ensure the single-line format is retained.

Python removing a comma from last line within a for loop

Id like to know what it takes to remove a comma from the last line within a for loop in python. When I run the script it gives me the below output(after code section). I want to remove the comma at the end of fourth line "{"{#MACRO}":"queue4"}," Please can someone help?
By the way if there is a better way to construct the block please share the ideas. I'm a beginner and like to learn. :)
Code:
import json
import urllib
import string
Url= "http://guest:guest#localhost:55672/api/queues"
Response = urllib.urlopen(Url)
Data = Response.read()
def Qlist(Name):
Text = ''' {{"{{#MACRO}}":"{Name}"}},'''.format(Name=Name)
print Text
X_json = json.loads(Data)
print '''{
"data":['''
for i in X_json:
VV = i['name']
Qlist(VV)
print ''']
}'''
Below is the Output:
{
"data":[
{"{#MACRO}":"queue1"},
{"{#MACRO}":"queue2"},
{"{#MACRO}":"queue3"},
{"{#MACRO}":"queue4"},
]
}
Thanks so much
You can modify your loop as follows.
# Create and initialize a dictionary (Associative Array)
# data['data'] is an empty list.
# Variable name (data in this case) can be anything you want.
# 'data' is a key. notice the quotations around. it's not a variable.
# I used 'data' as the key, becasue you wanted your final output to include that part.
data = {"data": []}
for i in X_json:
# We are not calling the data dictionary here.
# We are accessing the empty list we have created inside the `data` dict (above) using data['data'] syntax.
# We can use the append function to add an item to a list.
# We create a new dictionary for every `name` item found in your json array and
# append that new dictionary to the data['data'] list.
data['data'].append({"{#MACRO}": i['name']})
print(json.dumps(data))
# or print json.dumps(data, indent=True)
Read more about json.dumps() here. You can read more about python's list and dictionary here
Don't print inside Qlist - instead return a value; then you can join all returned values using comma as separator:
def Qlist(Name):
Text = ''' {{"{{#MACRO}}":"{Name}"}}'''.format(Name=Name)
return Text
print '''{
"data":[''' +
',\n'.join([ Qlist(i['name']) for i in X_json ]) +
''']
}'''
And anyway, using json.dumps is likely a better idea.

How can I read a text file and replace numbers?

If I have many of these in a text file;
<Vertex> 0 {
-0.597976 -6.85293 8.10038
<UV> { 0.898721 0.149503 }
<RGBA> { 0.92549 0.92549 0.92549 1 }
}
...
<Vertex> 1507 {
12 -5.3146 -0.000708352
<UV> { 5.7487 0.180395 }
<RGBA> { 0.815686 0.815686 0.815686 1 }
}
How can I read through the text file and add 25 to the first number in the second row? (-0.597976 in Vertex 0)
I have tried splitting the second line's text at each space with .split(' '), then using float() on the third element, and adding 25, but I don't know how to implicitly select the line in the text file.
Try to ignore the lines that start with "<", for example:
L=["<Vertex> 0 {",
"-0.597976 -6.85293 8.10038",
"<UV> { 0.898721 0.149503 }",
"<RGBA> { 0.92549 0.92549 0.92549 1 }"
]
for l in L:
if not l.startswith("<"):
print l.split(' ')[0]
Or if you read your data from a file:
f = open("test.txt", "r")
for line in f:
line = line.strip().split(' ')
try:
print float(line[0]) + 25
except:
pass
f.close()
The hard way is to use Python Lex/Yacc tools.
The hardest (did you expect "easy"?) way is to make a custom function recognizing tokens (tokens would be <Vertex>, numbers, bracers, <UV> and <RGBA>; token separators would be spaces).
I'm sorry but what you're asking is a mini language if you cannot guarantee the entries respect the CR and LFs.
Another ugly (and even harder!) way is, since you don't use recursion in that mini language, using regex. But the regex solution would be long and ugly in the same way and amount (trust me: really long one).
Try using this library: Python Lex/Yacc since what you need is to parse a language, and even when regex is possible to use here, you'll end with an ugly and unmaintainable one. YOU HAVE TO LEARN THE TIPS of language parsing to use this. Have a look Here
If the verticies will always be on the line after , you can look for that as a marker, then read the next line. If you read the second line, .strip() leading and trailing whitespace, then .split() by the space character, you will have a list of your three verticies, like so (assuming you have read the line into a string varaible line:
>>> line = line.strip()
>>> verticies = line.split(' ')
>>> verticies
['-0.597976', '-6.85293', '8.10038']
What now? Call float() on the first item in your list, then add 25 to the result.
The real challenge here is finding the <Vertex> marker and reading the subsequent line. This looks like a homework assignment, so I'll let you puzzle that out a bit first!
If your file is well-formatted, then you should be able to parse through the file pretty easily. Assuming <Vertex> is always on a line proceeding a line with just the three numbers, you could do this:
newFile = []
while file:
line = file.readline()
newFile.append(line)
if '<Vertex>' in line:
line = file.readline()
entries = line.strip().split()
entries[0] = str(25+float(entries[0]))
line = ' ' + ' '.join(entries)
newFile.append(line)
with open(newFileName, 'w') as fileToWrite:
fileToWrite.writelines(newFile)
This syntax looks like a Panda3d .egg file.
I suggest you use Panda's file load, modify, and save functions to work on the file safely; see https://www.panda3d.org/manual/index.php/Modifying_existing_geometry_data
Something like:
INPUT = "path/to/myfile.egg"
def processGeomNode(node):
# something using modifyVertexData()
def main():
model = loader.loadModel(INPUT)
for nodePath in model.findAllMatches('**/+GeomNode').asList():
processGeomNode(nodePath.node())
if __name__=="__main__":
main()
It is a Panda3D .egg file. The easiest and most reliable way to modify data in it is by using Panda3D's EggData API to parse the .egg file, modify the desired value through these structures, and write it out again, without loss of data.

How can I elegantly combine/concat files by section with python?

Like many an unfortunate programmer soul before me, I am currently dealing with an archaic file format that refuses to die. I'm talking ~1970 format specification archaic. If it were solely up to me, we would throw out both the file format and any tool that ever knew how to handle it, and start from scratch. I can dream, but that unfortunately that won't resolve my issue.
The format: Pretty Loosely defined, as years of nonsensical revisions have destroyed almost all back compatibility it once had. Basically, the only constant is that there are section headings, with few rules about what comes before or after these lines. The headings are sequential (e.g. HEADING1, HEADING2, HEADING3,...), but not numbered and are not required (e.g HEADING1, HEADING3, HEADING7). Thankfully, all possible heading permutations are known. Here's a fake example:
# Bunch of comments
SHOES # First heading
# bunch text and numbers here
HATS # Second heading
# bunch of text here
SUNGLASSES # Third heading
...
My problem: I need to concatenate multiple of these files by these section headings. I have a perl script that does this quite nicely:
while(my $l=<>) {
if($l=~/^SHOES/i) { $r=\$shoes; name($r);}
elsif($l=~/^HATS/i) { $r=\$hats; name($r);}
elsif($l=~/^SUNGLASSES/i) { $r=\$sung; name($r);}
elsif($l=~/^DRESS/i || $l=~/^SKIRT/i ) { $r=\$dress; name($r);}
...
...
elsif($l=~/^END/i) { $r=\$end; name($r);}
else {
$$r .= $l;
}
print STDERR "Finished processing $ARGV\n" if eof;
}
As you can see, with the perl script I basically just change where a reference points to when I get to a certain pattern match, and concatenate each line of the file to its respective string until I get to the next pattern match. These are then printed out later as one big concated file.
I would and could stick with perl, but my needs are becoming more complex every day and I would really like to see how this problem can be solved elegantly with python (can it?). As of right now my method in python is basically to load the entire file as a string, search for the heading locations, then split up the string based on the heading indices and concat the strings. This requires a lot of regex, if-statements and variables for something that seems so simple in another language.
It seems that this really boils down to a fundamental language issue. I found a very nice SO discussion about python's "call-by-object" style as compared with that of other languages that are call-by-reference.
How do I pass a variable by reference?
Yet, I still can't think of an elegant way to do this in python. If anyone can help kick my brain in the right direction, it would be greatly appreciated.
That's not even elegant Perl.
my #headers = qw( shoes hats sunglasses dress );
my $header_pat = join "|", map quotemeta, #headers;
my $header_re = qr/$header_pat/i;
my ( $section, %sections );
while (<>) {
if (/($header_re)/) { name( $section = \$sections{$1 } ); }
elsif (/skirt/i) { name( $section = \$sections{'dress'} ); }
else { $$section .= $_; }
print STDERR "Finished processing $ARGV\n" if eof;
}
Or if you have many exceptions:
my #headers = qw( shoes hats sunglasses dress );
my %aliases = ( 'skirt' => 'dress' );
my $header_pat = join "|", map quotemeta, #headers, keys(%aliases);
my $header_re = qr/$header_pat/i;
my ( $section, %sections );
while (<>) {
if (/($header_re)/) {
name( $section = \$sections{ $aliases{$1} // $1 } );
} else {
$$section .= $_;
}
print STDERR "Finished processing $ARGV\n" if eof;
}
Using a hash saves the countless my declarations you didn't show.
You could also do $header_name = $1; name(\$sections{$header_name}); and $sections{$header_name} .= $_ for a bit more readability.
I'm not sure if I understand your whole problem, but this seems to do everything you need:
import sys
headers = [None, 'SHOES', 'HATS', 'SUNGLASSES']
sections = [[] for header in headers]
for arg in sys.argv[1:]:
section_index = 0
with open(arg) as f:
for line in f:
if line.startswith(headers[section_index + 1]):
section_index = section_index + 1
else:
sections[section_index].append(line)
Obviously you could change this to read or mmap the whole file, then re.search or just buf.find for the next header. Something like this (untested pseudocode):
import sys
headers = [None, 'SHOES', 'HATS', 'SUNGLASSES']
sections = defaultdict(list)
for arg in sys.argv[1:]:
with open(arg) as f:
buf = f.read()
section = None
start = 0
for header in headers[1:]:
idx = buf.find('\n'+header, start)
if idx != -1:
sections[section].append(buf[start:idx])
section = header
start = buf.find('\n', idx+1)
if start == -1:
break
else:
sections[section].append(buf[start:])
And there are plenty of other alternatives, too.
But the point is, I can't see anywhere where you'd need to pass a variable by reference in any of those solutions, so I'm not sure where you're stumbling on whichever one you've chosen.
So, what if you want to treat two different headings as the same section?
Easy: create a dict mapping headers to sections. For example, for the second version:
headers_to_sections = {None: None, 'SHOES': 'SHOES', 'HATS': 'HATS',
'DRESSES': 'DRESSES', 'SKIRTS': 'DRESSES'}
Now, in the code that doessections[section], just do sections[headers_to_sections[section]].
For the first, just make this a mapping from strings to indices instead of strings to strings, or replace sections with a dict. Or just flatten the two collections by using a collections.OrderedDict.
My deepest sympathies!
Here's some code (please excuse minor syntax errors)
def foundSectionHeader(l, secHdrs):
for s in secHdrs:
if s in l:
return True
return False
def main():
fileList = ['file1.txt', 'file2.txt', ...]
sectionHeaders = ['SHOES', 'HATS', ...]
sectionContents = dict()
for section in sectionHeaders:
sectionContents[section] = []
for file in fileList:
fp = open(file)
lines = fp.readlines()
idx = 0
while idx < len(lines):
sec = foundSectionHeader(lines[idx]):
if sec:
idx += 1
while not foundSectionHeader(lines[idx], sectionHeaders):
sectionContents[sec].append(lines[idx])
idx += 1
This assumes that you don't have content lines which look like "SHOES"/"HATS" etc.
Assuming you're reading from stdin, as in the perl script, this should do it:
import sys
import collections
headings = {'SHOES':'SHOES','HATS':'HATS','DRESS':'DRESS','SKIRT':'DRESS'} # etc...
sections = collections.defaultdict(str)
key = None
for line in sys.stdin:
sline = line.strip()
if sline not in headings:
sections[headings.get(key)].append(sline)
else:
key = sline
You'll end up with a dictionary where like this:
{
None: <all lines as a single string before any heading>
'HATS' : <all lines as a single string below HATS heading and before next heading> ],
etc...
}
The headings list does not have to be defined in the some order as the headings appear in the input.

Pyparsing: How can I parse data and then edit a specific value in a .txt file?

my data is located in a .txt file (no, I can't change it to a different format) and it looks like this:
varaiablename = value
something = thisvalue
youget = the_idea
Here is my code so far (taken from the examples in Pyparsing):
from pyparsing import Word, alphas, alphanums, Literal, restOfLine, OneOrMore, \
empty, Suppress, replaceWith
input = open("text.txt", "r")
src = input.read()
# simple grammar to match #define's
ident = Word(alphas + alphanums + "_")
macroDef = ident.setResultsName("name") + "= " + ident.setResultsName("value") + Literal("#") + restOfLine.setResultsName("desc")
for t,s,e in macroDef.scanString(src):
print t.name,"=", t.value
So how can I tell my script to edit a specific value for a specific variable?
Example:
I want to change the value of variablename, from value to new_value.
So essentially variable = (the data we want to edit).
I probably should make it clear that I don't want to go directly into the file and change the value by changing value to new_value but I want to parse the data, find the variable and then give it a new value.
Even though you have already selected another answer, let me answer your original question, which was how to do this using pyparsing.
If you are trying to make selective changes in some body of text, then transformString is a better choice than scanString (although scanString or searchString are fine for validating your grammar expression by looking for matching text). transformString will apply token suppression or parse action modifications to your input string as it scans through the text looking for matches.
# alphas + alphanums is unnecessary, since alphanums includes all alphas
ident = Word(alphanums + "_")
# I find this shorthand form of setResultsName is a little more readable
macroDef = ident("name") + "=" + ident("value")
# define values to be updated, and their new values
valuesToUpdate = {
"variablename" : "new_value"
}
# define a parse action to apply value updates, and attach to macroDef
def updateSelectedDefinitions(tokens):
if tokens.name in valuesToUpdate:
newval = valuesToUpdate[tokens.name]
return "%s = %s" % (tokens.name, newval)
else:
raise ParseException("no update defined for this definition")
macroDef.setParseAction(updateSelectedDefinitions)
# now let transformString do all the work!
print macroDef.transformString(src)
Gives:
variablename = new_value
something = thisvalue
youget = the_idea
For this task you do not need to use special utility or module
What you need is reading lines and spliting them in list, so first index is left and second index is right side.
If you need these values later you might want to store them in dictionary.
Well here is simple way, for somebody new in python. Uncomment lines whit print to use it as debug.
f=open("conf.txt","r")
txt=f.read() #all text is in txt
f.close()
fwrite=open("modified.txt","w")
splitedlines = txt.splitlines():
#print splitedlines
for line in splitedlines:
#print line
conf = line.split('=')
#conf[0] is what it is on left and conf[1] is what it is on right
#print conf
if conf[0] == "youget":
#we get this
conf[1] = "the_super_idea" #the_idea is now the_super_idea
#join conf whit '=' and write
newline = '='.join(conf)
#print newline
fwrite.write(newline+"\n")
fwrite.close()
Actually, you should have a look at the config parser module
Which parses exactly your syntax (you need only to add [section] at the beginning).
If you insist on your implementation, you can create a dictionary :
dictt = {}
for t,s,e in macroDef.scanString(src):
dictt[t.name]= t.value
dictt[variable]=new_value
ConfigParser
import ConfigParser
config = ConfigParser.RawConfigParser()
config.read('example.txt')
variablename = config.get('variablename', 'float')
It'll yell at you if you don't have a [section] header, though, but it's ok, you can fake one.

Categories