string mismatch in Python even if they have same value - python

Instead of keeping keys in my application I intent to read the keys from local file system into a variable (array of strings) and use those array elements in my oAuth APIs. However, when i used keys (in plaintext) as argument to OAuth APIs, authentication succeeds. BUT authentication failed when same value in read into a variable from file & that variable is passed to OAuth API.
Tried comparing the key value and variable value t find out they don't match though they same exactly same.
Input file looks as below:
$cat .keys
k1='jFOMZ0bI60fDAEKw53lYCj2r4'
k2='LNkyPehneIi8HeqTg1ji74H42jFkkBxZolRfzNFmaJKwLg7R7E'
secret_keys=[]
def keys_io():
key_file = open('/Users/homie/.keys', 'r+')
for key in range(1,5):
secret_keys.append(key_file.readline().split("=")[1])
print secret_keys[0]
print (secret_keys[0] == "jFOMZ0bI60fDAEKw53lYCj2r4")
keys_io()
Output:
jFOMZ0bI60fDAEKw53lYCj2r4
False
What am i missing here?

You should strip the key that you read from the file, as it has a trailing \n:
print(secret_keys[0].strip() == "jFOMZ0bI60fDAEKw53lYCj2r4")
Or do it when reading it:
for key in range(1,5):
secret_keys.append(key_file.readline().split("=")[1].strip())

If leading-trailing characters are bugging you, remove them with slicing, i.e [1:-1] to remove first-last quotations.
I also refactored your function a bit:
def keys_io():
with open('.keys', 'r+') as f:
for line in f:
secret_keys.append(line.split('=')[1].strip()[1:-1])
print secret_keys[0]
print (secret_keys[0] == "jFOMZ0bI60fDAEKw53lYCj2r4"
Use a context manager to open/close your file automatically.
Use for line in <opened_file> instead of other methods if you need to examine all lines.
Use strip() without arguments to remove unwanted space.
After these changes, the keys_io file works like a charm for me when using the .key file you presented.

When you read a text file from Python, you need to escape the new line character first. Another problem is you have single quote in between the input text. So you need to make change to:
secret_keys.append(key_file.readline().strip().split("=")[1])
and
if(secret_keys[0] == "\'jFOMZ0bI60fDAEKw53lYCj2r4\'"):

Related

What is the purpose of readline().strip()?

What is the purpose of readline().strip() (especially in the below code)?
Context:
I was taking a look at the following code:
op = open('encyin.txt', 'r')
n, q = op.readline().split()
n = int(n)
q = int(q)
dic = {}
for i in range(1, n + 1):
dic[str(i)]=(op.readline().strip())
And trying to interpret it.
My Interpretation:
The start is simple enough - it opens a file encyin.txt in read mode. It takes input - n & p - from the line, the .split() separating the two inputs. They are then classified as integers, and an empty list dict is created?
From there, a for loop is utilised.
But what does the last line mean? I am not familiar with (a) readline().strip() and (b) how this affects list dict and the values of the input:
For Example
If ency.txt was the following:
6 5
1151
723
1321
815
780
931
What happens to the other numbers from the 2nd line downwards? Does the readline().split assign them a line number? Does it add it to the list dict, a bit like .append?
What does the last line mean of the top code do? I am not familiar with (a) readline().strip() and (b) how this affects list dict and the values of the input:
In your text file, you have these things called whitespace characters. Often, these are spaces or enters ('\n') that you want to get rid of. The strip() helps you remove these whitespace characters.
If you were to print the numbers after reading them and without stripping, you would get:
number1
number2
number3
...
Because you haven't removed the hidden 'enter' character.
When reading a python script and you come across some function that you don't know, your goal should first to be understand the function out of context, and then you can figure out what they are doing in context.
The first port of call for understanding builtin/standard library functions (as opposed to functions from some extra library) should be the python docs. When the docs fail you, move on to other sources (there are plenty).
In this case, you want to know what op.readline() does. Well, what is op? I would go to open, and see that it creates a file object, which tells you that the actual implementation used is in io. Here we can search the page for readline.
What do the docs have to say about readline?
Read and return one line from the stream.
Here, I would assume, since it's a text file, "a line from the stream" is a string object (but you could always open a python interpreter to check), and look up string.strip(), which says:
Return a copy of the string with the leading and trailing characters removed. The chars argument is a string specifying the set of characters to be removed. If omitted or None, the chars argument defaults to removing whitespace.
Now put them together. They call (op.readline().strip()).
We know op is a "file object" using io
io's readline reads a single line from the stream
some_string.strip() called without parameters removes all whitespace from the start and end of some_string
Although python uses duck-typing, objects still have types/behaviours and understanding code often involves knowing what kind of object you are dealing with at any point so you can look into how it should work.
For example, if you know something is a dictionary, but you don't know what a dictionary is, you should search the docs for some info and try to understand what it does out of context first.
op = open('encyin.txt', 'r')
n, q = op.readline().split()
n = int(n)
q = int(q)
dic = {}
for i in range(1, n + 1):
# Here you're creating a key-value pair using the str value of the loop variable
# i as the dictionary key i.e. key dic[str(i)] creates the key, and the value is
# op.readline().strip(). strip() is a str method that removes trailing characters.
# the default is to remove whitespace at the beginning and ends of the string.
# These spaces get trimmed off if the method is called
dic[str(i)]=(op.readline().strip())
https://docs.python.org/3/library/stdtypes.html?highlight=str#str.strip
readline() returning a single line as string from your file.
ex: for the given txt file info:
Danni Loss
Shani Amari
Michele favarotti
readline() will return the first line:
Danni Loss\n
then there is a use of strip() removes all empty chars from the start and end of the string, so you will get:
Danni Loss
.readline() reads a line from a file. The result includes a trailing '\n'.
.strip() removes all leading & trailing whitespace (e.g. the above-mentioned '\n') from a string.
Thus, the last line of code dic[str(i)]=(op.readline().strip()) does the following:
Reads line from the open file
Strips whitespace from the line
Stores the stripped line in the dictionary using the index (converted to string) as a key

Print on a single line not working Python 3

I'm very new to programming and am working on some code to extract data from a bunch of text files. I've been able to do this however the data is not useful to me in Excel. Therefore, I would like to print it all on a single line and separate it by a special character, which I can then delimit in Excel.
Here is my code:
import os
data=['Find me','find you', 'find us']
with open('C:\\Users\\Documents\\File.txt', 'r') as inF:
for line in inF:
for a in data:
string=a
if string in line:
print (line,end='*') #print on same line
inF.close()
So basically what I'm doing is finding if a keyword is on that line and then printing that line if it is.
Even though I have print(,end='*'), I don't get the print on a single line. It outputs:
Find me
*find you
*find us
Where is the problem? (I'm using Python 3.5.1)
Your immediate problem is that you're not removing the newline characters from your lines before printing them. The usual way to do this is with strip(), eg:
print(line.strip(), end='*')
You'll also print multiple copies of the line if more than one of your special phrases appear in the line. To avoid that, add a break statement after your print, or (better, but a more advanced construct that might not make sense until you're used to generator expressions) use if any(keyword in line for keyword in data):
You also don't need to explicitly close the input file - the point of the with open(...) as ...: context manager is that it closes the file when exiting it.
And I would avoid using string as a variable name - it doesn't tell anyone anything about what the variable is used for, and it can cause confusion if you end up using the built-in string module for anything. It's not as bad as shadowing a built-in constructor like list, but it's worth avoiding. Especially since it does nothing for you here, you can just use if a in line: here if you don't want to use the any() version above.
In addition to all that, if your data is not extremely large (and I hope it's not if you're trying to fit it all on one line) you'll get tidier code and avoid the trailing delimiter by using the .join() method on strings, eg something like:
import os
data=['Find me','find you', 'find us']
with open('C:\\Users\\Documents\\File.txt', 'r') as inF:
print "*".join(line.strip() for line in inF if any(keyword in line for keyword in data))

Python variables stored in text files not working, but should be

I've settled on a text-file based save system for my game, storing the values of required variables with keywords - for example, the password that tells the game which chapter to play. However, it appears to be malfunctioning, and I can't see why.
Before starting the game, we have:
if not os.file.isfile('TSGsave{0}.txt'.format(name)):
TSGsave=open('TSGsave{0}.txt'.format(name),'wt')
TSGsave.write('\nw5CT$n<jfW=-#J%4Ya5##')
TSGsave.close()
(the keyword used is a bunch of jibberish so that the user can't change it knowing what's going to happen). This adds w5CT$n<jfW=-#J%4Ya5## to the text file. We then have:
for i in range (len(lines)):
if 'w5CT$n<jfW' in lines[i]:
findpass=lines[i]
for i in range (len(findpass)):
if findpass[i]=='=':
cutfrom=i+1
password=findpass[cutfrom:len(findpass)]
to retrieve the variable (which can change, so it can't be written in as definite value). I know it works, because I added print (password) to the code and it returned -#J%4Ya5##. Then to start the corresponding chapter, the code is:
if password=='-#J%4Ya5##':
but it isn't starting the indented block. In the shell, the program ends and goes back to the >>> line.
If there is a way to fix this code, great - but another code to do the same thing would work just as well.
Your lines contain newlines, and these are being included. Strip these from the line:
findpass = lines[i].rstrip('\n')
Printing a value with a newline in it will simply add an extra black line after the print. Always use the repr() function to produce a Python representation of strings to see such characters:
>>> print '-#J%4Ya5##\n'
-#J%4Ya5##
>>> print repr('-#J%4Ya5##\n')
'-#J%4Ya5##\n'
Your parsing code is overly complicated; you can use str.split() or str.partition() to split your password from the line instead. You should just loop over the lines list directly rather than produce indices with range():
for line in lines:
if 'w5CT$n<jfW' in line:
password = line.partition('=')[2].rstrip('\n')

more ids getting deleted from resulting text file

I have an id in invalid_change variable which I am trying to delete from the input file "list.txt" and create a file results.txt as below..can anyone provide inputs on how can this be fixed?I have a sample input and expected output below..
'''
INPUT(list.txt:-
350882 348521 350166
346917 352470
360049
EXPECTEDOUTPUT(results.txt):-
350882 348521 350166
346917
360049
'''
invalid_list=['352470','12345']
f_write = open('results.txt', 'wb')
with open('list.txt','r') as f :
for line in f :
#delete the whole line if any invalid gerrit is presnet
gerrit_list = line.strip().split(' ')
ifvalid = True
for gerrit in gerrit_list:
try: # check if invalid gerrit is present
invalid_gerrit.index(invalid_change)
ifvalid = False
break
except:
pass
if ifvalid:
f_write.write(line)
f_write.close()
The problem is that when you reach this line:
346917 352470
the ifvalid variable is set to False and thus this specific line (even if adjusted) is not being written to the output file.
Possible solutions:
Modify string eg. using regular expressions. They will do replacing.
Do not skip writing "invalid" line to a file, just prepare it without the ID you want to remove (as in solution no. 1).
Let us know, if you have any questions.
Other problems in your code are:
diaper anti-pattern (you catch all the exceptions, including NameError, while you should only catch ValueError, or rather performing test including in keyword) - this is the reason you do not see errors related to lack of invalid_change and invalid_gerrit variables,
instead of opening and closing f_write, you could use it also in with statement you are already using, if you have Python 2.7+ or 3.1+,
str.split() splits by default by whitespaces, and removes empty strings from result, so instead of writing line.strip().split(' ') you could just write line.split(),
Each item in gerrit_list is an individual number. You don't need to find whether the invalid change is contained within the number. In fact, that can cause bugs. You can just directly iterate through the items in gerrit_list and throw away the ones that match invalid_list:
for line in f:
gerrit_list = line.split()
valid_gerrits = [gerrit for gerrit in gerrit_list if gerrit not in invalid_list]
if valid_gerrits:
newline = ' '.join(valid_gerrits) + '\n'
f_write.write(newline)

Python: How to ignore #comment lines when reading in a file

In Python, I have just read a line form a text file and I'd like to know how to code to ignore comments with a hash # at the beginning of the line.
I think it should be something like this:
for
if line !contain #
then ...process line
else end for loop
But I'm new to Python and I don't know the syntax
you can use startswith()
eg
for line in open("file"):
li=line.strip()
if not li.startswith("#"):
print line.rstrip()
I recommend you don't ignore the whole line when you see a # character; just ignore the rest of the line. You can do that easily with a string method function called partition:
with open("filename") as f:
for line in f:
line = line.partition('#')[0]
line = line.rstrip()
# ... do something with line ...
partition returns a tuple: everything before the partition string, the partition string, and everything after the partition string. So, by indexing with [0] we take just the part before the partition string.
EDIT:
If you are using a version of Python that doesn't have partition(), here is code you could use:
with open("filename") as f:
for line in f:
line = line.split('#', 1)[0]
line = line.rstrip()
# ... do something with line ...
This splits the string on a '#' character, then keeps everything before the split. The 1 argument makes the .split() method stop after a one split; since we are just grabbing the 0th substring (by indexing with [0]) you would get the same answer without the 1 argument, but this might be a little bit faster. (Simplified from my original code thanks to a comment from #gnr. My original code was messier for no good reason; thanks, #gnr.)
You could also just write your own version of partition(). Here is one called part():
def part(s, s_part):
i0 = s.find(s_part)
i1 = i0 + len(s_part)
return (s[:i0], s[i0:i1], s[i1:])
#dalle noted that '#' can appear inside a string. It's not that easy to handle this case correctly, so I just ignored it, but I should have said something.
If your input file has simple enough rules for quoted strings, this isn't hard. It would be hard if you accepted any legal Python quoted string, because there are single-quoted, double-quoted, multiline quotes with a backslash escaping the end-of-line, triple quoted strings (using either single or double quotes), and even raw strings! The only possible way to correctly handle all that would be a complicated state machine.
But if we limit ourselves to just a simple quoted string, we can handle it with a simple state machine. We can even allow a backslash-quoted double quote inside the string.
c_backslash = '\\'
c_dquote = '"'
c_comment = '#'
def chop_comment(line):
# a little state machine with two state varaibles:
in_quote = False # whether we are in a quoted string right now
backslash_escape = False # true if we just saw a backslash
for i, ch in enumerate(line):
if not in_quote and ch == c_comment:
# not in a quote, saw a '#', it's a comment. Chop it and return!
return line[:i]
elif backslash_escape:
# we must have just seen a backslash; reset that flag and continue
backslash_escape = False
elif in_quote and ch == c_backslash:
# we are in a quote and we see a backslash; escape next char
backslash_escape = True
elif ch == c_dquote:
in_quote = not in_quote
return line
I didn't really want to get this complicated in a question tagged "beginner" but this state machine is reasonably simple, and I hope it will be interesting.
I'm coming at this late, but the problem of handling shell style (or python style) # comments is a very common one.
I've been using some code almost everytime I read a text file.
Problem is that it doesn't handle quoted or escaped comments properly. But it works for simple cases and is easy.
for line in whatever:
line = line.split('#',1)[0].strip()
if not line:
continue
# process line
A more robust solution is to use shlex:
import shlex
for line in instream:
lex = shlex.shlex(line)
lex.whitespace = '' # if you want to strip newlines, use '\n'
line = ''.join(list(lex))
if not line:
continue
# process decommented line
This shlex approach not only handles quotes and escapes properly, it adds a lot of cool functionality (like the ability to have files source other files if you want). I haven't tested it for speed on large files, but it is zippy enough of small stuff.
The common case when you're also splitting each input line into fields (on whitespace) is even simpler:
import shlex
for line in instream:
fields = shlex.split(line, comments=True)
if not fields:
continue
# process list of fields
This is the shortest possible form:
for line in open(filename):
if line.startswith('#'):
continue
# PROCESS LINE HERE
The startswith() method on a string returns True if the string you call it on starts with the string you passed in.
While this is okay in some circumstances like shell scripts, it has two problems. First, it doesn't specify how to open the file. The default mode for opening a file is 'r', which means 'read the file in binary mode'. Since you're expecting a text file it is better to open it with 'rt'. Although this distinction is irrelevant on UNIX-like operating systems, it's important on Windows (and on pre-OS X Macs).
The second problem is the open file handle. The open() function returns a file object, and it's considered good practice to close files when you're done with them. To do that, call the close() method on the object. Now, Python will probably do this for you, eventually; in Python objects are reference-counted, and when an object's reference count goes to zero it gets freed, and at some point after an object is freed Python will call its destructor (a special method called __del__). Note that I said probably: Python has a bad habit of not actually calling the destructor on objects whose reference count drops to zero shortly before the program finishes. I guess it's in a hurry!
For short-lived programs like shell scripts, and particularly for file objects, this doesn't matter. Your operating system will automatically clean up any file handles left open when the program finishes. But if you opened the file, read the contents, then started a long computation without explicitly closing the file handle first, Python is likely to leave the file handle open during your computation. And that's bad practice.
This version will work in any 2.x version of Python, and fixes both the problems I discussed above:
f = open(file, 'rt')
for line in f:
if line.startswith('#'):
continue
# PROCESS LINE HERE
f.close()
This is the best general form for older versions of Python.
As suggested by steveha, using the "with" statement is now considered best practice. If you're using 2.6 or above you should write it this way:
with open(filename, 'rt') as f:
for line in f:
if line.startswith('#'):
continue
# PROCESS LINE HERE
The "with" statement will clean up the file handle for you.
In your question you said "lines that start with #", so that's what I've shown you here. If you want to filter out lines that start with optional whitespace and then a '#', you should strip the whitespace before looking for the '#'. In that case, you should change this:
if line.startswith('#'):
to this:
if line.lstrip().startswith('#'):
In Python, strings are immutable, so this doesn't change the value of line. The lstrip() method returns a copy of the string with all its leading whitespace removed.
I've found recently that a generator function does a great job of this. I've used similar functions to skip comment lines, blank lines, etc.
I define my function as
def skip_comments(file):
for line in file:
if not line.strip().startswith('#'):
yield line
That way, I can just do
f = open('testfile')
for line in skip_comments(f):
print line
This is reusable across all my code, and I can add any additional handling/logging/etc. that I need.
I know that this is an old thread, but this is a generator function that I
use for my own purposes. It strips comments no matter where they
appear in the line, as well as stripping leading/trailing whitespace and
blank lines. The following source text:
# Comment line 1
# Comment line 2
# host01 # This host commented out.
host02 # This host not commented out.
host03
host04 # Oops! Included leading whitespace in error!
will yield:
host02
host03
host04
Here is documented code, which includes a demo:
def strip_comments(item, *, token='#'):
"""Generator. Strips comments and whitespace from input lines.
This generator strips comments, leading/trailing whitespace, and
blank lines from its input.
Arguments:
item (obj): Object to strip comments from.
token (str, optional): Comment delimiter. Defaults to ``#``.
Yields:
str: Next uncommented non-blank line from ``item`` with
comments and leading/trailing whitespace stripped.
"""
for line in item:
s = line.split(token, 1)[0].strip()
if s:
yield s
if __name__ == '__main__':
HOSTS = """# Comment line 1
# Comment line 2
# host01 # This host commented out.
host02 # This host not commented out.
host03
host04 # Oops! Included leading whitespace in error!""".split('\n')
hosts = strip_comments(HOSTS)
print('\n'.join(h for h in hosts))
The normal use case will be to strip the comments from a file (i.e., a hosts file, as in my example above). If this is the case, then the tail end of the above code would be modified to:
if __name__ == '__main__':
with open('aa.txt', 'r') as f:
hosts = strip_comments(f)
for host in hosts:
print('\'%s\'' % host)
A more compact version of a filtering expression can also look like this:
for line in (l for l in open(filename) if not l.startswith('#')):
# do something with line
(l for ... ) is called "generator expression" which acts here as a wrapping iterator that will filter out all unneeded lines from file while iterating over it. Don't confuse it with the same thing in square brakets [l for ... ] which is a "list comprehension" that will first read all the lines from the file into memory and only then will start iterating over it.
Sometimes you might want to have it less one-liney and more readable:
lines = open(filename)
lines = (l for l in lines if ... )
# more filters and mappings you might want
for line in lines:
# do something with line
All the filters will be executed on the fly in one iteration.
Use regex re.compile("^(?:\s+)*#|(?:\s+)") to skip the new lines and comments.
I tend to use
for line in lines:
if '#' not in line:
#do something
This will ignore the whole line, though the answer which includes rpartition has my upvote as it can include any information from before the #
a good thing to get rid of coments that works for both inline and on a line
def clear_coments(f):
new_text = ''
for line in f.readlines():
if "#" in line: line = line.split("#")[0]
new_text += line
return new_text

Categories