Basic Python Issue / removing whitespace - python

I am struggling in a python undergraduate class that should have had fewer modules: for a grade, I have a code that reads a formatted file and "prints" a table. The problem is, the last entry of the table has a trailing space at the end. My print statement is
for time in movieTiming[m]:
print(time, end=" ")
I really have no idea what to do here: i have a list that contains something like "11:30", "10:30", "9:00", and it should be printed as 11:30 10:30 9:00 (with no space after the 9:00). I have tried to join my list, but really, most of the concepts I need to do all of this were never even communicated or taught in the class. I guess that's how it goes, but I'm struggling. My approach is to appropriate existing code, try to understand it, and learn that way, but it's not making any sense to me.
I am taking Java I at the same time, and Java makes sense to me because the pace of the Java course is about 1/2 of the pace of the Python class: 2x the modules means 1/2 the time. If anyone can help, thank you.
Here's what I have (I'll remove the notes if it's not helpful?)
# First we open the file named "movies.csv" using the open()
f = open(input())
# f.readlines() reads the contents of the file and stores each line as a separate element in a list named movies.
movies = f.readlines()
# Next we declare 2 dictionaries named movieTiming and movieRating.
# movieTiming will store the timing of each movie.
# The key would be the movie name and the value would be the list of timings of the movie.
movieTiming = {}
# movieRating will store the rating of each movie.
# key would be the movie name and the value would be the rating of the respective movie.
movieRating = {}
# Now we traverse through the movies list to fill our dictionaries.
for m in movies:
# First we split each line into 3 parts that is, we split the line whenever a comma(",") occurs.
# split(",") would return a list of splitted words.
# For example: when we split "16:40,Wonders of the World,G", it returns a list ["16:40","Wonders of the World","G"]
movieDetails = m.split(",")
# movieDetails[1] indicates the movie name.
# So if the movie name is not present in the dictionary then we initialize the value with an empty list.
#need a for loop
if(movieDetails[1] not in movieTiming):
movieTiming[movieDetails[1]] = []
# movieDetails[0] indicates the timing of the movie.
# We append the time to the existing list of the movie.
movieTiming[movieDetails[1]].append(movieDetails[0])
# movieDetails[2] indicates the rating of the movie.
# We use strip() since a new line character will be appended at the end of the movie rating.
# So to remove the new line character at the end we use strip() and we assign the rating to the respective movie.
movieRating[movieDetails[1]] = movieDetails[2].strip()
# Now we traverse the movieRating dictionary.
for m in movieRating:
# In -44.44s, negative sign indicates left justification.
# 44 inidcates the width assigned to movie name.
# .44 indicates the number of characters allowed for the movie name.
# s indicates the data type string.
# print() generally prints a message and prints a new line at the end.
# So to avoid this and print the movie name, rating and timing in the same line, we use end=" "
# end is used to print all in the same line separated by a space.
print("%-44.44s"%m,"|","%5s"%movieRating[m],"|",end=" ")
# Now we traverse through the movieTiming[m] which indicates the list of timing for the particular movie m.
for time in movieTiming[m]:
print(time, end=" ")
# This print() will print a new line to print the next movie details in the new line.
print()

Instead of multiple calls to print, create a single space-delimited string with ' '.join and print that.
print(' '.join(movieTiming[m]))
As you've noted, printing a space between list elements is different from printing a space after each element. While you can play around with list indices to figure out which element is the last element and avoid printing a space after it, the join method already handles the corner cases for you.
Similar to what you tried, though, consider an approach not of printing a space after all but the last element, but printing a space before all but the first.
print(movieTiming[m][0], end='')
for t in movieTiming[m][1:]:
print(f' {t}', end=''
print()
I mention this not because you should consider it an alternative to str.join, but because it helps to think about your problem in different ways.

This might help:
my_list = ['11:00', '12:30', '13:00']
joined = ' '.join(my_list)
print(joined)
# 11:00 12:30 13:00

Supposed you have:
time = ["19:30","19:00","18:00"]
then you could apply the list as separate arguments:
print(*time)
You can, as always, control the separator by setting the sep keyword argument:
print(*time, sep=', ')
Unless you need the joined string for something else, this is the easiest method. Otherwise, use str.join():
joined_string = ' '.join([str(v) for v in time])
print(joined_string)

Related

Removing '\n' from a string without using .translate, .replace or strip()

I'm making a simple text-based game as a learning project. I'm trying to add a feature where the user can input 'save' and their stats will be written onto a txt file named 'save.txt' so that after the program has been stopped, the player can then upload their previous stats and play from where they left off.
Here is the code for the saving:
user inputs 'save' and class attributes are saved onto the text file as text, one line at a time
elif first_step == 'save':
f = open("save.txt", "w")
f.write(f'''{player1.name}
{player1.char_type} #value is 'Wizard'
{player1.life}
{player1.energy}
{player1.strength}
{player1.money}
{player1.weapon_lvl}
{player1.wakefulness}
{player1.days_left}
{player1.battle_count}''')
f.close()
But, I also need the user to be able to load their saved stats next time they run the game. So they would enter 'load' and their stats will be updated.
I'm trying to read the text file one line at a time and then the value of that line would become the value of the relevant class attribute in order, one at a time. If I do this without converting it first to a string I get issues, such as some lines being skipped as python is reading 2 lines as one and putting them altogether as a list.
So, I tried the following:
In the below example, I'm only showing the data from the class attributes 'player1.name' and 'player1.char_type' as seen above as to not make this question as short as possible.
elif first_step == 'load':
f = open("save.txt", 'r')
player1.name_saved = f.readline() #reads the first line of the text file and assigns it's value to player1.name_saved
player1.name_saved2 = str(player1.name_saved) # converts the value of player1.name_saved to a string and saves that string in player1.name_saved2
player1.name = player1.name_saved2 #assigns the value of player1.name_saved to the class attribute player1.name
player1.char_type_saved = f.readlines(1) #reads the second line of the txt file and saves it in player1.char_type_saved
player1.char_type_saved2 = str(player1.char_type_saved) #converts the value of player1.char_type_saved into a string and assigns that value to player1.char_type_saved2
At this point, I would assign the value of player1.char_type_saved2 to the class attribute player1.char_type so that the value of player1.char_type enables the player to load the previous character type from the last time they played the game. This should make the value of player1.char_type = 'Wizard' but I'm getting '['Wizard\n']'
I tried the following to remove the brackets and \n:
final_player1.char_type = player1.char_type_saved2.translate({ord(c): None for c in "[']\n" }) #this is intended to remove everything from the string except for Wizard
For some reason, the above only removes the square brackets and punctuation marks but not \n from the end.
I then tried the following to remove \n:
final_player1.char_type = final_player1.char_type.replace("\n", "")
final_player1.char_type is still 'Wizard\n'
I've also tried using strip() but I've been unsuccessful.
If anyone could help me with this I would greatly appreciate it. Sorry if I have overcomplicated this question but it's hard to articulate it without lots of info. Let me know if this is too much or if more info is needed to answer.
If '\n' is always at the end it may be best to use:
s = 'wizard\n'
s = s[:-1]
print(s, s)
Output:
wizard wizard
But I still think strip() is best:
s = 'wizard\n'
s = s.strip()
print(s, s)
Output:
wizard wizard
Normaly it should work with just
char_type = "Wizard\n"
char_type.replace("\n", "")
print(char_type)
The output will be "Wizard"

Sort with re.search() - Python

i have some problems with solving the follwoing problem.
I have to *.txt files in both files are cities from austria. In the first file "cities1" are the cities are ordered by population.
The first file (cities1.txt) is looking like this:
1.,Vienna,Vienna,1.840.573
2.,Graz,Styria,273.838
3.,Linz,Upper Austria,198.181
4.,Salzburg,Salzburg,148.420
5.,Innsbruck,Tyrol,126.851
The second file (cities2.txt) is looking like this:
"Villach","Carinthia",60480,134.98,501
"Innsbruck","Tyrol",126851,104.91,574
"Graz","Styria",273838,127.57,353
"Dornbirn","Vorarlberg",47420,120.93,437
"Vienna","Vienna",1840573,414.78,151
"Linz","Upper Austria",198181,95.99,266
"Klagenfurt am Woerthersee","Carinthia",97827,120.12,446
"Salzburg","Salzburg",148420,65.65,424
"Wels","Upper Austria",59853,45.92,317
"Sankt Poelten","Lower Austria",52716,108.44,267
What i like to do, or in other words what i should do is, the first file cities1.txt is already sorted. I only need the second element of every line. That means i only need the name of the city. For example from the line 2.,Graz,Styria,273.838, i only need Graz.
Than second i should print out the area of the city, this is the fourth element of every line in cities2.txt. That means, for example from the third line "Graz","Styria",273838,127.57,353, i only need 127.57.
At the end the console should display the following:
Vienna,414.78
Graz,127.57
Linz,95.99
Salzburg,65.65
Innsbruck,104.91
So, my problem now is, how can i do this, if i only allowed to use the re.search() method. Cause the second *.txt file is not in the same order and i have to bring the cities in the same order as in the first file that this will work, or?
I know, it would be much easier to use re.split() because than you are able to compare the list elements form both files. But I'm not allowed to do this.
I hope someone can help me and sorry for the long text.
Here's an implementation based on my earlier comment:
with open('cities2.txt') as c:
D = {}
for line in c:
t = line.split(',')
cn = t[0].strip('"')
D[cn] = t[-2]
with open('cities1.txt') as d:
for line in d:
t = line.split(',')
print(f'{t[1]},{D[t[1]]}')
Note that this may not be robust. If there's a city name in cities1.txt that does not exist in cities2.txt then you'll get a KeyError
This is just a hint, it's your university assignment after all.
import re
TEST = '2.,Graz,Styria,273.838'
RE = re.compile('^[^,]*,([^,]*),')
if match := RE.search(TEST):
print(match.group(1)) # 'Graz'
Let's break down the regexp:
^ - start of line
[^,]* - any character except a comma - repeated 0 or more times
this is the first field
, - one comma character
this is the field separator
( - start capturing, we are interested in this field
[^,]* - any character except a comma - repeated 0 or more times
this is the second field
) - stop capturing
, - one comma character
(don't care about the rest of line)

How to find keywords in a file using module re

I am looking to make a program that can pick out car information from a file using module re. The user is asked questions about the car that he wants to view and if the input is not in the file I should display an error message and loop the code again if the user wants to. I am having difficulty trying to find the inputs in the file: this is the code so far:
import re
import random
myList = ([])
car = input("What car do you want to view?");
myList.insert(1, car)
model = input("What car model is it of");
myList.insert(2, model)
fuelTy = input("What fuel type is it: diseal or petrol");
myList.insert(3, fuelTy)
engSize = input("What engine size is it : eg 2l");
myList.insert(4, engSize)
rnd = (int(random.randrange(50000000)) + 1)
with open("car.txt", "r") as carfile:
for line in carfile:
if all(myList.lower() in re.findall('\w+', line.lower()) for myList in carfile):
splitted_line = line.split(':')
print(splitted_line)
if not myList not in carfile:
print("We don't have the car available currently. Please contact the head office with the case number " + str(rnd))
Cho2 = input("Would you like to see anything yes or no").lower
if Cho2 == "yes":
print("OK")
elif Cho2 == "no":
print("End of program")
Text file is:
bmw : X6 : 3.4l : Engine size 4395cc: petrol: 0-62mph in 4.8s: gear type automatic : 5 doors : economy 29mpg : top speed 155 mph
audi : Q7 : 3.0l : Engine size 2967cc: disel: 0-62mph in 6.5s: gear type automatic : 5 doors : economy: 48mpg : top speed 145 mph
honda : CRV : 2.0l: Engine size 1997cc: petrol : 0-62mph in 10.0s: gear type manual : 5 doors : economy 30mpg : top speed 18 mph
if all(myList.lower() in re.findall('\w+', line.lower()) for myList in carfile):
In this line, you are re-defining myList to be a line in the file. But you have an outer loop (for line in carfile) that does the same thing.
Change this to eliminate the for expression, and you'll be on the right track:
if all(myList.lower() in re.findall('\w+', line.lower())):
FWIW, this is going to be very hit-or-miss, because you have things like engine size that use different measures (cc vs. l) in the file.
Next, please be aware that you can (and probably should, as a matter of avoiding errors) use .append() in order to grow a list, instead of .insert(). The difference is that append does not require you to keep track of an index, which in cases like this is not benefiting you (you don't make use of the position information at any time) and is a source of error if you copy/paste a block of code to add a new field.
myList.append(engSize) # was myList.insert(4, engSize)
Also, you should probably give users the option of not entering a field, and skip searching if they don't enter it. (Just don't append the field if it's empty, perhaps?)
engSize = input("What engine size is it : eg 2l")
if engSize: myList.append(engSize)
EDIT
Okay, just got done for the day, back to this program. :-)
There are some more problems, but let's take care of the "all requires an iterableissue first. If you look at the docs for [all`](https://docs.python.org/3/library/functions.html#all), it says
all(iterable)
So we need to restructure the test to give all an iterable (a list, tuple, view, or other expression that can be iterated), or we need to stop using all.
Well, we are trying to iterate over myList, so it should be possible to come up with an iterable. Let's start with that in mind:
if all(s for s in myList):
In fact, we can put the .lower() back in - that made sense. So:
if all(s.lower() for s in myList):
Now, let's treat s.lower() as a word (which it is) and search for it in the input line. What we are doing is converting our previous string expression, s.lower(), into a boolean expression: word in list, within the context of the iteration we already have. This will be a different flavor of the in keyword:
if all( (EXPR) for s in myList):
if all((s.lower() in re.findall('\w+', line.lower())) for s in myList):
When I make this change, I can match the make of the car.
There are some problems with the logic, as well. You want to match the user query to a car type. If you can't match that, then you want to print a message about "We don't have the car ..." But you can't get that test (not matching) in one line. If you could get that test into one line, you could probably get the searching part into one line as well. (Not always, but it's worth looking!)
Instead, just keep track of whether or not you found the car:
found_car = False
for line in carfile:
if ...
found_car = True
break
if not found_car:
print("We don't have the car ...")
Next, let's make the program run longer (for testing if nothing else). You are doing for loops, so I assume you can do a while loop. Let's add a loop around the whole thing to keep going until the user types quit:
while True:
make = input("What make of car do you want (or type 'quit')? ")
if make == 'quit':
break
if make:
myList.append(make)
Finally, let's take a look at your regular expression. You are using \w+, which will match "word characters" (whatever those are) one or more times.
That's a good start for things like "audi" and "honda", but word characters don't include the period ('.') or the hyphen ('-'), both of which appear in your data.
Instead, try changing your regex to match either a word character or a dot, one or more times:
re.findall('[\w.]+', ...)
Good luck!
You have bugs!
First, you are initializing myList = ([]) as a tuple containing a single empty list, so append() or insert() aren't going to work. Try myList = [].
Second, you're getting the indexes wrong in your myList.insert(...) statements. Just use myList.append(...) instead, no need to worry about indexes that way.
Then, try replacing (not tested...):
if all(myList.lower() in re.findall('\w+', line.lower()) for myList in carfile):
... with:
if all (item.lower() in re.findall('\w+', line.lower()) for item in myList):
A simple optimization which also makes the code more readable:
line_words = set(re.findall('\w+', line.lower()))
if all(item.lower() in line_words for item in myList):

In Python,if startswith values in tuple, I also need to return which value

I have an area codes file I put in a tuple
for line1 in area_codes_file.readlines():
if area_code_extract.search(line1):
area_codes.append(area_code_extract.search(line1).group())
area_codes = tuple(area_codes)
and a file I read into Python full of phone numbers.
If a phone number starts with one of the area codes in the tuple, I need to do to things:
1 is to keep the number
2 is to know which area code did it match, as need to put area codes in brackets.
So far, I was only able to do 1:
for line in txt.readlines():
is_number = phonenumbers.parse(line,"GB")
if phonenumbers.is_valid_number(is_number):
if line.startswith(area_codes):
print (line)
How do I do the second part?
The simple (if not necessarily highest performance) approach is to check each prefix individually, and keep the first match:
for line in txt:
is_number = phonenumbers.parse(line,"GB")
if phonenumbers.is_valid_number(is_number):
if line.startswith(area_codes):
print(line, next(filter(line.startswith, area_codes)))
Since we know filter(line.startswith, area_codes) will get exactly one hit, we just pull the hit using next.
Note: On Python 2, you should start the file with from future_builtins import filter to get the generator based filter (which will also save work by stopping the search when you get a hit). Python 3's filter already behaves like this.
For potentially higher performance, the way to both test all prefixes at once and figure out which value hit is to use regular expressions:
import re
# Function that will match any of the given prefixes returning a match obj on hit
area_code_matcher = re.compile(r'|'.join(map(re.escape, area_codes))).match
for line in txt:
is_number = phonenumbers.parse(line,"GB")
if phonenumbers.is_valid_number(is_number):
# Returns None on miss, match object on hit
m = area_code_matcher(line)
if m is not None:
# Whatever matched is in the 0th grouping
print(line, m.group())
Lastly, one final approach you can use if the area codes are of fixed length. Rather than using startswith, you can slice directly; you know the hit because you sliced it off yourself:
# If there are a lot of area codes, using a set/frozenset will allow much faster lookup
area_codes_set = frozenset(area_codes)
for line in txt:
is_number = phonenumbers.parse(line,"GB")
if phonenumbers.is_valid_number(is_number):
# Assuming lines that match always start with ###
if line[:3] in area_codes_set:
print(line, line[:3])

Trying to use Python script to add strings to file

I have a spanish novel, in a plain textfile, and I want to make a Python script that puts a translation in brackets after difficult words. I have a list of the words (with translations) I want to do this with in a separate text file, which I have tried to format correctly.
I've forgotten everything I knew about Python, which was very little to begin with, so I'm struggling.
This is a script someone helped me with:
bookin = (open("C:\Users\King Kong\Documents\_div_tekstfiler_\coc_es.txt")).read()
subin = open("C:\Users\King Kong\Documents\_div_tekstfiler_\cocdict.txt")
for line in subin.readlines():
ogword, meaning = line.split()
subword = ogword + " (" + meaning + ")"
bookin.replace(ogword, subword)
ogword = ogword.capitalize()
subword = ogword + " (" + meaning + ")"
bookin.replace(ogword, subword)
subin.close()
bookout = open("fileout.txt", "w")
bookout.write(bookin)
bookout.close()
When I ran this, I got this error message:
Traceback (most recent call last):
File "C:\Python27\translscript_secver.py", line 4, in <module>
ogword, meaning = line.split()
ValueError: too many values to unpack
The novel pretty big, and the dictionary I've made consists of about ten thousand key value pairs.
Does this mean there's something wrong with the dictionary? Or it's too big?
Been researching this a lot, but I can't seem to make sense of it. Any advice would be appreciated.
line.split() in ogword, meaning = line.split() returns a list, and in this case it may be returning more than 2 values. Write your code in a way that can handle more than two values. For instance, by assigning line.split() to a list and then asserting that the list has two items:
mylist = line.split()
assert len(mylist) == 2
ogword, meaning = line.split()[:2]
line.split() return a list of words (space separated token) in line. The error you get suggest that somewhere, your dictionnary contains more than just pair. You may add trace message to locate the error (see below).
If your dictionnary contains richer definitions than synonym, you may use following lines, which put the first word in ogword and following ones in meaning.
words = line.split()
ogword, meaning = words[0], " ".join(words[1:])
If your dictionary syntax is more complex (composed ogword), you have to rely on an explicit separator. You can still use split to divide your lines (line.split("=") will split a line on "=" characters)
Edit: to ignore and display bad lines, replace ogword, meaning = line.split() with
try:
ogword,meaning = line.split()
except:
print "wrong formated line:", line
continue
split()
returns a single list, ie one item, you are trying to assign this one thing to two variables.
It will work if the number of items in the list is equal to the number of variables on the left hand side of the assignment statement. I.e., the list is unpacked and the individual parts are assigned to the variables on the left hand side.
In this case, as pointed out by #Josvic Zammit, the problem can occur if there are more than 2 items in the list and can not properly "unpacked" and assigned.

Categories