用Python程序统计文件中的括号?

0 投票
7 回答
4524 浏览
提问于 2025-04-15 16:51

我想修复一个函数,通过这个函数我可以计算符号:(,),[,],这些符号出现了多少次。如果(的数量和)的数量相等,并且[的数量和]的数量相等,那么我就有了有效的语法!

这是我第一次尝试,但结果让我有点失望:

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 个回答

1

我觉得如果你把:

            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)

1

去掉'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,这样是没问题的。

这样做应该能让你离解决问题更近一步。

4

请原谅我这段回复有点长。

如果我理解得没错,你想要检查括号是否配对正确。你在问题中提到了一种简单的计数方法,但正如其他人所指出的,这种方法无法处理像“([)]”这样的情况。

我还想对你代码的其他方面提出一些建设性的意见。

首先,最好从命令行获取文件名,而不是提示用户输入。这样在开发程序时,你可以轻松多次运行它,而不需要每次都输入文件名。你现在的方式是:

$ 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()

撰写回答