用Python程序统计文件中的括号?
我想修复一个函数,通过这个函数我可以计算符号:(,),[,],这些符号出现了多少次。如果(的数量和)的数量相等,并且[的数量和]的数量相等,那么我就有了有效的语法!
这是我第一次尝试,但结果让我有点失望:
filename=input("Give a file name:")
def parenthesis(filename):
try:
f=open(filename,'r')
except (IOError):
print("The file",filename,"does not exist!False! Try again!")
else:
while True:
for line in filename:
line=f.readline()
if line=='(':
c1=line.count('(')
elif line==')':
c2=line.count(')')
elif line=='[':
c3=line.count('[')
elif line==']':
c4=line.count(']')
elif line=='':
break
if c1==c2:
print("Line: Valid Syntax")
elif c1!=c2:
print("Line: InValid Syntax")
elif c3==c4:
print("Line: Valid Syntax")
elif c3!=c4:
print("Line: InValid Syntax")
finally:
f.close()
parenthesis(filename)
7 个回答
我觉得如果你把:
if line=='(':
c1=line.count('(')
elif line==')':
c2=line.count(')')
elif line=='[':
c3=line.count('[')
elif line==']':
c4=line.count(']')
elif line=='':
break
改成类似这样的:
SearchFor = ['(', ')', '[', ']']
d = {}
for itm in SearchFor:
d[itm] = line.count(itm)
# Then do the comparison
if d['['] == d[']'] and d['('] == d[')']:
print "Valid Syntax"
else:
print "Invalid Syntax" #You could look at each to find the exact cause.
还有其他人提到的那个While True:
,我之前没注意到这一点。:0)
去掉'while True'这一行和下面这部分:
elif line=='':
break
然后把这个:
for line in filename:
line=f.readline()
换成这个:
for line in f:
现在你就可以开始逐行读取文件里的内容了。
接下来,把这些东西:
if line=='(':
c1=line.count('(')
替换成:
c1+=line.count('(')
if和elif这两行的作用是防止你在不该计数的时候去计数。如果这一行没有你想要的内容,计数就会是0,这样是没问题的。
这样做应该能让你离解决问题更近一步。
请原谅我这段回复有点长。
如果我理解得没错,你想要检查括号是否配对正确。你在问题中提到了一种简单的计数方法,但正如其他人所指出的,这种方法无法处理像“([)]”这样的情况。
我还想对你代码的其他方面提出一些建设性的意见。
首先,最好从命令行获取文件名,而不是提示用户输入。这样在开发程序时,你可以轻松多次运行它,而不需要每次都输入文件名。你现在的方式是:
$ python foo.py
输入文件名:data
[一些输出]
$ python foo.py
输入文件名:data
[一些输出]
$ python foo.py
输入文件名:data
[一些输出]
你每次都需要输入文件名。你不需要多次输入运行程序的命令。第一次之后,你可以使用箭头键从命令历史中获取它。如果你从命令行获取文件名,你可以这样做:
$ python foo.py testfile
[一些输出]
$ python foo.py testfile
[一些输出]
$ python foo.py testfile
[一些输出]
这样,当你第二次测试时,只需按上箭头和回车键就可以了。这是一个小小的便利,但很重要:在开发软件时,即使是小事也可能让人烦恼。就像在长途步行时脚下有一粒沙子:你在前几公里可能感觉不到,但走久了就会很痛苦。
在Python中,要访问命令行参数,你需要用到 sys.argv
列表。对你程序的相关修改如下:
import sys
filename = sys.argv[1]
如果你确实想要提示用户输入,应该使用其他方法,而不是内置的 input
函数。因为它会把用户输入的内容当作Python表达式来解释,这会引发各种问题。你可以使用 sys.stdin.readline
来读取。
无论如何,现在我们已经把文件名安全地存储在 filename
变量中。是时候对它做点事情了。你的 parentheses
函数几乎做了所有事情,而经验表明,这通常不是最佳做法。每个函数应该只做一件事,但要把这件事做好。
我建议你将打开和关闭文件的部分与实际计数分开。这样可以简化计数的逻辑,因为它不需要担心其他的事情。代码如下:
import sys
def check_parentheses(f):
pass # we'll come to this later
def main():
filename = sys.argv[1]
try:
f = file(filename)
except IOError:
sys.stderr.write('Error: Cannot open file %s' % filename)
sys.exit(1)
check_parentheses(f)
f.close()
main()
除了重新安排代码外,我还改变了一些其他的地方。首先,我把错误信息写入标准错误输出。这是正确的做法,能减少用户在重定向输出时的意外情况。(如果你对此不太理解,没关系,暂时接受这个观点就好。)
其次,如果出现错误,我会用 sys.exit(1)
退出程序。这告诉启动程序的人它失败了。在Unix shell中,这让你可以做如下操作:
if python foo.py inputfile
then
echo "inputfile is OK!"
else
echo "inputfile is BAD!"
fi
当然,shell脚本可能会做一些比仅仅报告成功或失败更有趣的事情。例如,它可能会删除所有损坏的文件,或者给写这些文件的人发邮件,要求他们修复。关键是,你这个检查程序的编写者不需要关心这些。你只需正确设置程序的退出代码,让编写shell脚本的人去处理其他的事情。
下一步是实际读取文件的内容。这可以通过多种方式完成。最简单的方法是逐行读取,像这样:
for line in f:
# do something with the line
然后我们需要查看每一行中的每个字符:
for line in f:
for c in line:
# do something with the character
现在我们准备开始检查括号了。正如其他人所建议的,使用栈是合适的数据结构。栈基本上是一个列表(或数组),你可以在一端添加项目,并以相反的顺序取出它们。想象一下它就像一堆硬币:你可以把硬币放在顶部,也可以取出最上面的硬币,但你不能从中间或底部取出。
(当然,你可以这样做,这是一种很酷的技巧,但计算机是简单的东西,遇到魔术时会感到不安。)
我们将使用Python列表作为栈。要添加一个项目,我们使用列表的 append
方法,要移除则使用 pop
方法。示例如下:
stack = list()
stack.append('(')
stack.append('[')
stack.pop() # this will return '['
stack.pop() # this will return '('
要查看栈顶的项目,我们使用 stack[-1]
(换句话说,就是列表中的最后一个项目)。
我们使用栈的方式是:当我们找到一个左括号('(')、左中括号('[')或左花括号('{')时,我们把它放入栈中。当我们找到一个右括号时,我们检查栈顶的项目,确保它与右括号匹配。如果不匹配,我们就打印错误。像这样:
def check_parentheses(f):
stack = list()
for line in f:
for c in line:
if c == '(' or c == '[' or c == '{':
stack.append(c)
elif c == ')':
if stack[-1] != '(':
print 'Error: unmatched )'
else:
stack.pop()
elif c == ']':
if stack[-1] != '[':
print 'Error: unmatched ]'
else:
stack.pop()
elif c == '}':
if stack[-1] != '{':
print 'Error: unmatched }'
else:
stack.pop()
这样就能找到各种类型的不匹配括号。我们可以稍微改进一下,报告出现问题的行和列。我们需要一个行号和列号计数器。
def error(c, line_number, column_number):
print 'Error: unmatched', c, 'line', line_number, 'column', column_number
def check_parentheses(f):
stack = list()
line_number = 0
for line in f:
line_number = line_number + 1
column_number = 0
for c in line:
column_number = column_number + 1
if c == '(' or c == '[' or c == '{':
stack.append(c)
elif c == ')':
if stack[-1] != '(':
error(')', line_number, column_number)
else:
stack.pop()
elif c == ']':
if stack[-1] != '[':
error(']', line_number, column_number)
else:
stack.pop()
elif c == '}':
if stack[-1] != '{':
error('}', line_number, column_number)
else:
stack.pop()
还要注意,我添加了一个辅助函数 error
来实际打印错误信息。如果你想更改错误信息,现在只需在一个地方进行修改。
另一个需要注意的地方是,处理关闭符号的情况都非常相似。我们也可以把这部分做成一个函数。
def check(stack, wanted, c, line_number, column_number):
if stack[-1] != wanted:
error(c, line_number, column_number)
else:
stack.pop()
def check_parentheses(f):
stack = list()
line_number = 0
for line in f:
line_number = line_number + 1
column_number = 0
for c in line:
column_number = column_number + 1
if c == '(' or c == '[' or c == '{':
stack.append(c)
elif c == ')':
check(stack, '(', ')', line_number, column_number)
elif c == ']':
check(stack, '[', ']', line_number, column_number)
elif c == '}':
check(stack, '{', '}', line_number, column_number)
程序可以进一步优化,但现在这些就足够了。我会在最后附上完整的代码。
请注意,这个程序只关心各种类型的括号。如果你真的想检查整个Python程序的语法正确性,你需要解析所有的Python语法,这相当复杂,超出了一个Stack Overflow回答的范围。如果你确实想要这样,请提出后续问题。
完整程序如下:
import sys
def error(c, line_number, column_number):
print 'Error: unmatched', c, 'line', line_number, 'column', column_number
def check(stack, wanted, c, line_number, column_number):
if stack[-1] != wanted:
error(c, line_number, column_number)
else:
stack.pop()
def check_parentheses(f):
stack = list()
line_number = 0
for line in f:
line_number = line_number + 1
column_number = 0
for c in line:
column_number = column_number + 1
if c == '(' or c == '[' or c == '{':
stack.append(c)
elif c == ')':
check(stack, '(', ')', line_number, column_number)
elif c == ']':
check(stack, '[', ']', line_number, column_number)
elif c == '}':
check(stack, '{', '}', line_number, column_number)
def main():
filename = sys.argv[1]
try:
f = file(filename)
except IOError:
sys.stderr.write('Error: Cannot open file %s' % filename)
sys.exit(1)
check_parentheses(f)
f.close()
main()