Python misinterpreting sys.argv[1] when called with * as filename? - python-3.x

I'm doing a task for school where i need my program to do four things:
(1) It should take in a filename as an argument and count lines, words and chars.
(2) It should accept the argument *.py and scan all the .py files in current directory for the same as in (1)
(3) It should accept the argument * and scan ALL files in current directory for the same as in (1).
(4) I should be able to call it from command line as simply filename followed by argument. Example: Filename is Hello_world.py and takes one argument. Then it should look like this:
Hello_world arg
When I call with a specific filename as in (1) it seems to be working here:
else:
a, b, c, n = counting(call)
print('There are: ' + str(a) + ' lines, ' + str(b) + ' words and ' + str(c) + ' chars in ' + n)
but not working for (2) and (3) here:
if call == '*':
import os
for fname in os.listdir(os.getcwd()):
a, b, c, n = counting(fname)
print('There are: ' + str(a) + ' lines, ' + str(b) + ' words and ' + str(c) + ' chars in ' + n)
elif call == '*.py':
import glob
for fname in glob.glob('*.py'):
a, b, c, n = counting(fname)
print('There are: ' + str(a) + ' lines, ' + str(b) + ' words and ' + str(c) + ' chars in ' + n)
I tried printing out sys.argv[1] when using * and *.py as argument and then it prints out the first file in the directory and not * or *.py. Why is python interpreting * as a filename in the directory instead of simply the string *? How do I work around this? I tried moving the imports untill it actually entered the if/elif, but that did no change.
When it comes to making the script callable as in (4) I'm clueless, but that is not my main concern at this moment.
Entire script here:
import sys
def counting(fname):
lines = words = chars = 0
f= open(fname)
for line in f:
lines += 1
words += len(line.split())
chars += len(line)
name = f.name
f.close()
return lines, words, chars, name
def main():
call = sys.argv[1]
print(sys.argv[1])
a = b = c = 0
if call == '*':
import os
for fname in os.listdir(os.getcwd()):
a, b, c, n = counting(fname)
print('There are: ' + str(a) + ' lines, ' + str(b) + ' words and ' + str(c) + ' chars in ' + n)
elif call == '*.py':
import glob
for fname in glob.glob('*.py'):
a, b, c, n = counting(fname)
print('There are: ' + str(a) + ' lines, ' + str(b) + ' words and ' + str(c) + ' chars in ' + n)
else:
a, b, c, n = counting(call)
print('There are: ' + str(a) + ' lines, ' + str(b) + ' words and ' + str(c) + ' chars in ' + n)
if __name__ == '__main__':
main()
All help greatly appreciated. Thanks in advance!

When you run a program like:
prog *
there's no guarantee that the * will make it through to the program unscathed.
In fact, many shells will explicitly doe expansion so that the * gets turned into a number of arguments (such as every file in the current directory, for example).
In fact, you have to go out of your way to prevent this, by escaping special characters so the shell won't interpret them, such as:
prog \*
But. if you want to process all the files anyway, you should probably let the shell do what it wants, then process each of the expanded arguments separately.
In Python, that would be something like:
import sys
for arg in sys.argv[1:]: # [0] is the program name.
print(arg)

Related

Line by line comparison of text files - length error

#create empty list for wbm.txt
wbm_lines = []
#read the file and append each line to the empty list
with open('wbm.txt') as file_to_read:
for line in file_to_read:
wbm_lines.append(line)
i = 1
#create empty list for docu.txt
docu_lines = []
#read the file and append each line to the empty list
with open('docu.txt') as file_to_read:
for line in file_to_read:
docu_lines.append(line)
if len(wbm_lines) > len(docu_lines):
for line in wbm_lines:
if line != docu_lines:
print("Line " + str(i) + " do not match")
print('wbm.txt ' + wbm_lines[i])
print('docu.txt ' + docu_lines[i])
i += 1
else:
for line in docu_lines:
if line != wbm_lines:
try:
print("Line " + str(i) + " do not match")
print('wbm.txt ' + wbm_lines[i])
print('docu.txt ' + docu_lines[i])
i += 1
except:
continue
But I am getting an error:
print('docu.txt ' + docu_lines[i])
IndexError: list index out of range
The problem is that the two text files have different lengths. How can I resolve this?
Ideally, I would like to print something like --- in place of the non-existent line, while printing the other line normally.

How do I split sentence on 'and'?

I am trying to split my sentence on 'and' but some of the results looks like this
My code
string = 'I am handling it because it is difficult and confusing'
string.split('and')
Results
['I am h', 'ling it because it is difficult ', ' confusing']
I am trying to get this. How do I do it?
['I am handling it because it is difficult ', ' confusing']
Try doing
string.split(" and ")
It will only pick the word.
But if you need spaces, this function/loop will do(tested):
add_spaces(x):
x[0] += ' '
for i in range(1, len(x) - 1):
x[i] = ' ' + x[i]
x[i] += ' '
x[-1] = ' ' + x[-1]

How to get a function value without running the whole function again

I would like to save the value of the list that was inputted earlier without having to reinput it again when I call the function to save it.
Preferrably without using OOP as it is currently outside of my learning module but if its inevitable then it is welcomed as well for self learning.
def createQuiz():
quiz = []
for i in range(2):
quiz.append(str(i+1) + ') ' + input('Please input Question ' + str(i+1) + ':\n'))
for j in range(4):
quiz.append(chr(97+j) + '. ' + input('Please input option ' + str(j+1) + ':\n'))
quiz.append('Answer: ' + input('Please input Answer(A,B,C,D):\n'))
return quiz
def saveQuiz():
with open('quiz.txt', 'w') as file:
for i in createQuiz():
file.write(i)
file.write('\n')
def menu():
userinput = int(input())
if userinput == 1:
createQuiz()
elif userinput == 2:
saveQuiz()
I am expecting for it to save the value in the list into the file, however when I run saveQuiz() it would run the whole createQuiz() asking me to re input the value again.
your line
for i in createQuiz():
was calling for a new instance of quiz and had no reference to the quiz you made before. I think you meant to do this:
def createQuiz():
quiz = []
for i in range(2):
quiz.append(str(i+1) + ') ' + input('Please input Question ' + str(i+1) + ':\n'))
for j in range(2):
quiz.append(chr(97+j) + '. ' + input('Please input option ' + str(j+1) + ':\n'))
quiz.append('Answer: ' + input('Please input Answer(A,B,C,D):\n'))
return quiz
def saveQuiz(quiz):
with open('quiz.txt', 'w') as file:
for i in quiz:
file.write(i)
file.write('\n')
quiz_instance = createQuiz()
saveQuiz(quiz_instance)
which gave me a file 'quiz.txt' containing
1) q1
a. a
b. b
Answer: b
2) q2
a. a
b. b
Answer: a

How to fix the error "index out of range"

I have to split the string with some "" in the string
I am a beginner in python, plz help me QQ
With the problem that line3 shows "index out of range"
windows
data = input().split(',')
for i in range(len(data)):
for j in range(len(data[i])):
if data[i][j] == '"':
data[i] += "," + data[i + 1]
data.pop(i + 1)
if data[i + 1][j] == '"':
data[i] += "," + data[i + 1]
data.pop(i + 1)
print(data[i])
sample input:
'str10, "str12, str13", str14, "str888,999", str56, ",123,", 5'
sample output:
str10
"str12, str13"
str14
"str888,999"
str56
",123,"
5
Your error occurs if you acces a list/string behind its data. You are removing things and access
for i in range(len(data)):
...
data[i] += "," + data[i + 1]
If i ranges from 0 to len(data)-1 and you access data[i+1] you are outside of your data on your last i!
Do not ever modify something you iterate over, that leads to desaster. Split the string yourself, by iterating it character wise and keep in mind if you are currently inside " ... " or not:
data = 'str10, "str12, str13", str14, "str888,999", str56, ",123,", 5'
inside = False
result = [[]]
for c in data:
if c == ',' and not inside:
result[-1] = ''.join(result[-1]) # add .strip() to get rid of spaces on begin/end
result.append([])
else:
if c == '"':
inside = not inside
result[-1].append(c)
result[-1] = ''.join(result[-1]) # add .strip() to get rid of spaces on begin/end
print(result)
print(*result, sep = "\n")
Output:
['str10', ' "str12, str13"', ' str14', ' "str888,999"', ' str56', ' ",123,"', ' 5']
str10
"str12, str13"
str14
"str888,999"
str56
",123,"
5
Add .strip() to the join-lines to get rid of leading/trailing spaces:
result[-1] = ''.join(result[-1]).strip()

Multiplication tables will not work in my code

What is wrong with my function? I have to write a function that prints out the multiplication table for the specified number. For example: in multiTable(num) if num = 6, then the function should print:"see attached photo".. This is python by the way. Thank you in advance for the help.
Here is my code:
def multiTable(num):
empty=""
print('\t',end='')
for row in range(1,num):
for column in range(1,num):
empty = empty + (str(row*column) +'\t') + '\n'
print(empty)
Reset variable before each loop
First of all you need to reset value of empty variable before each inner loop
Your value should be initialized with empty string: empty = ''
range loops EXCLUISVE
To loop in given range use num + 1 as the ending point
Fixed code
def multiTable(num):
# Display top line
for i in range(1, num + 1):
print('\t' + str(i), end='')
print()
# Show mutiplication table
for row in range(1, num + 1):
# Start by multiplier on the left
empty = str(row) + '\t'
for column in range(1, num + 1):
empty = empty + str(row * column) + '\t'
print(empty)

Resources