2012-08-09 111 views
0

我正在寻找一种简单的方法来打开文件,并搜索每一行以查看该行是否有未封闭的parens和引号。如果该行有未封闭的parens /引号,我想将该行打印到文件中。我知道我可以用一个丑陋的if/for语句来做到这一点,但是我知道python可能有更好的方法来处理re模块(我什么都不知道)或其他东西,但是我不太了解这个语言这样做。确定一行是否有括号或引号未在python中关闭

谢谢!

编辑:一些示例行。如果您将其复制到记事本或某些内容并关闭文字换行(可能会很长),可能会更容易阅读。此外,文件中有超过10万行,所以有效的东西会很棒!

SL ID=0X14429A0B TY=STANDARD OWN=0X429A03 EXT=22 SLTK=0X1C429A0B MP=0X684003F0 SUB=0X24400007 
RT ID=0X18429A19 TY=CALONSC OWN=0X14429A0B EXLP=0X14429A0C CMDS=(N:0X8429A04,C:0X14429A0B) SGCC=2 REL=1 DESC="AURANT YD-INDSTRY LD" ATIS=T 
RT ID=0X18429A1A TY=CALONSC OWN=0X14429A0B EXLP=0X14429A08 CMDS=(R:0X8429A04,N:0X8429A05,C:0X14429A0B) SGCC=2 REL=2 DESC="AURANT YD TO TRK.1" ATIS=T 
RT ID=0X18429A1B TY=CALONSC OWN=0X14429A0B EXLP=0X14429A0A CMDS=(R:0X8429A04,R:0X8429A05,C:0X14429A0B) SGCC=2 REL=3 DESC="AURANT YD TO TRK.2" ATIS=T 
SL ID=0X14429A0C TY=STANDARD OWN=0X429A03 EXT=24 SLTK=0X1C429A0B MP=0X684003F1 SUB=0X24400007 
RT ID=0X18429A1C TY=CALONSC OWN=0X14429A0C EXLP=0X14429A0B CMDS=(N:0X8429A04,C:0X14429A0C) SGCC=2 REL=1 DESC="AURANT YD-INDSTRY LD" ATIS=T 
TK ID=0X1C429A08 TY=BLKTK OWN=0X429A03 EXT=12 LRMP=0X6C40BDAF LEN=5837 FSPD=60 PSPD=65 QUAL=TRK.1 MAXGE=0 MAXGW=0 JAL=4 ALT=12 SUB=0X24400007 RULES=(CTC:B:UP:0X24400007:485.7305:486.8359:T) LLON=-118.1766772 RLON=-118.1620059 LLAT=34.06838375 RLAT=34.07811764 LELE=416.6983 RELE=425.0596 ULAD=NO URAD=NO 
PT ID=0X20429A0F TY=STANDARD OWN=0X1C429A08 LTK=0X1C40006C RTK=0X1C429A0C REL=1 LEN=1 LQUAL="TRK.1" RQUAL="TRK.1" 
PTK OWN=0X1C429A08 PID=0X1C429A13 
+3

你的文件包含什么?某些编程语言的源代码? – 2012-08-09 19:20:54

+1

只有正则表达式可能在此处不起作用:考虑字符串文字或注释中的括号。 – 2012-08-09 19:21:51

+2

考虑'while(cond){if(cond2){}'。你的计划意见中没有提到,如果还是那样? – 2012-08-09 19:22:50

回答

6

如果你不认为会有向后无与伦比的括号(即“)(”),你可以这样做:

with open("myFile.txt","r") as readfile, open("outFile.txt","w") as outfile: 
    for line in readfile: 
     if line.count("(") != line.count(")") or line.count('"') % 2 != 0: 
      outfile.write(line) 

否则你将不得不指望他们一次一个检查不匹配,就像这样:

with open("myFile.txt","r") as readfile, open("outFile.txt","w") as outfile: 
    for line in readfile: 
     count = 0 
     for char in line: 
      if char == ")": 
       count -= 1 
      elif char == "(": 
       count += 1 
      if count < 0: 
       break 
     if count != 0 or text.count('"') % 2 != 0: 
      outfile.write(line) 

我想不出有更好的方法来处理它。 Python不支持递归正则表达式,因此正则表达式解决方案正确。这个

一两件事:鉴于您的数据,它可能是更好地把该进的功能和分割你的字符串,这很容易用正则表达式做的,是这样的:

import re 
splitre = re.compile(".*?=(.*?)(?:(?=\s*?\S*?=)|(?=\s*$))") 
with open("myFile.txt","r") as readfile, open("outFile.txt","w") as outfile: 
    for line in readfile: 
     def matchParens(text): 
      count = 0 
      for char in text: 
       if char == ")": 
        count -= 1 
       elif char == "(": 
        count += 1 
       if count < 0: 
        break 
      return count != 0 or text.count('"') % 2 != 0 
     if any(matchParens(text) for text in splitre.findall(line)): 
      outfile.write(line) 

原因为什么这可能更好呢是它会单独检查每个值对,这样,如果你在一个值对中有一个开放的元素,而在后一个值中有一个接近的元素,它不会认为没有不平衡的元素。

+0

嗯是的,你的第二块可能是要走的路。只要数它们可能在大多数时间工作,但我不知道。 – zakparks31191 2012-08-09 19:48:59

+0

+1不需要任何进口。也为了满足“简单”的要求。 – 2012-08-09 20:29:29

+0

它不会忽略带引号的字符串中的'()',即它把'“(”'视为不平衡的表达式。尽管@ZakParks在注释中提到它可能被当作错误。'pyparsing'解决方案表现得很好 – jfs 2012-08-09 21:35:03

-1

应该在每一行关闭parens和引号吗?如果是这样的话,你可以对括号和引号做一个简单的计数。如果它是平的,他们是匹配的。如果很奇怪,就会失踪。将该逻辑放入函数中,将文本文件的行转储到数组中,然后调用map来为数组中的每个字符串执行函数。

我的python的生锈,但这就是我会这样做,假设一切“应该”在同一行。

+0

,这将是一个简单的方法。如果没有发布另一种方法,我可能会用这个。虽然文件即时解析目前有1800万个字符,所以我认为一行一行,字符计数parens和引号字符可能会变得很慢,虽然。我会测试一下,看看会发生什么 – zakparks31191 2012-08-09 19:32:35

+0

如果这行包含这个:'ABC'lsdjfldjsf('?有'('和')'匹配的数字,但它们没有正确嵌套。 – PaulMcG 2012-08-09 19:39:56

+0

@ZakParks,手动计算字符会比在整个输入上执行正则表达式匹配更快,另外,有1,800万个字符是花生, – 2012-08-09 19:41:15

0

我只想做这样的事情:

for line in open(file, r): 
    if line.count('"') % 2 != 0 or line.count('(') != line.count(')'): 
     print(line) 

但我不能肯定会满足您的需求完全吻合。

更强大:

for line in open(file, r): 
    paren_count = 0 
    paren_count_start_quote = 0 
    quote_open = False 
    for char in line: 
     if char == ')': 
      paren_count -= 1 
     elif char == '(': 
      paren_count += 1 
     elif char == '"': 
      quote_open = not quote_open 
      if quote_open: 
       paren_count_start_quote = paren_count 
      elif paren_count != paren_count_start_quote: 
       print(line) 
       break 
     if paren_count < 0: 
      break 
    if quote_open or paren_count != 0: 
     print(line) 

没有测试强大的一个,应该工作,我想。它现在可以确保如下的东西:(“)”,在报价单内关闭一组parens打印行。

+0

可能实际上按照我的需要运行,我假设.count()解析整行?如果是这样看起来就是我所需要的 – zakparks31191 2012-08-09 19:41:02

+0

你是什么意思“自动解析”?它将整行作为字符串从文件中运行并通过检查。检查奇怪的东西,例如: 等等等等等等等等等等等等等等等 – DrGodCarl 2012-08-09 19:43:09

+0

它打破了()(')(' – jfs 2012-08-09 19:46:27

3
  1. 只是从一行中提取所有有趣的符号。
  2. 每当您收到一个关闭符号 时,将打开的符号推入堆栈并从堆栈弹出。
  3. 如果堆栈干净,符号平衡。如果 堆栈下溢或没有完全展开,则表示线路不平衡。

用于检查行的示例代码如下 - 我在第一行插入了一个杂散括号。

d = """SL ID=0X14429A0B TY=STANDARD OWN=0X429A(03 EXT=22 SLTK=0X1C429A0B MP=0X684003F0 SUB=0X24400007 
RT ID=0X18429A19 TY=CALONSC OWN=0X14429A0B EXLP=0X14429A0C CMDS=(N:0X8429A04,C:0X14429A0B) SGCC=2 REL=1 DESC="AURANT YD-INDSTRY LD" ATIS=T 
RT ID=0X18429A1A TY=CALONSC OWN=0X14429A0B EXLP=0X14429A08 CMDS=(R:0X8429A04,N:0X8429A05,C:0X14429A0B) SGCC=2 REL=2 DESC="AURANT YD TO TRK.1" ATIS=T 
RT ID=0X18429A1B TY=CALONSC OWN=0X14429A0B EXLP=0X14429A0A CMDS=(R:0X8429A04,R:0X8429A05,C:0X14429A0B) SGCC=2 REL=3 DESC="AURANT YD TO TRK.2" ATIS=T 
SL ID=0X14429A0C TY=STANDARD OWN=0X429A03 EXT=24 SLTK=0X1C429A0B MP=0X684003F1 SUB=0X24400007 
RT ID=0X18429A1C TY=CALONSC OWN=0X14429A0C EXLP=0X14429A0B CMDS=(N:0X8429A04,C:0X14429A0C) SGCC=2 REL=1 DESC="AURANT YD-INDSTRY LD" ATIS=T 
TK ID=0X1C429A08 TY=BLKTK OWN=0X429A03 EXT=12 LRMP=0X6C40BDAF LEN=5837 FSPD=60 PSPD=65 QUAL=TRK.1 MAXGE=0 MAXGW=0 JAL=4 ALT=12 SUB=0X24400007 RULES=(CTC:B:UP:0X24400007:485.7305:486.8359:T) LLON=-118.1766772 RLON=-118.1620059 LLAT=34.06838375 RLAT=34.07811764 LELE=416.6983 RELE=425.0596 ULAD=NO URAD=NO 
PT ID=0X20429A0F TY=STANDARD OWN=0X1C429A08 LTK=0X1C40006C RTK=0X1C429A0C REL=1 LEN=1 LQUAL="TRK.1" RQUAL="TRK.1" 
PTK OWN=0X1C429A08 PID=0X1C429A13""" 

def unbalanced(line): 
    close_symbols = {'"' : '"', '(': ")", '[': ']', "'" : "'"} 
    syms = [x for x in line if x in '\'"[]()'] 
    stack = [] 
    for s in syms: 
     try: 
      if len(stack) > 0 and s == close_symbols[stack[-1]]: 
       stack.pop() 
      else: 
       stack.append(s) 
     except: # catches stack underflow or closing symbol lookup 
      return True 
    return len(stack) != 0 


print unbalanced("hello 'there'() []") 
print unbalanced("hello 'there\"'() []") 
print unbalanced("][") 

lines = d.splitlines() # in your case you can do open("file.txt").readlines() 

print [line for line in lines if unbalanced(line)] 

对于大文件,你不想阅读所有的文件到内存中,因此使用的片段是这样,而不是:

with open("file.txt") as infile: 
    for line in infile: 
     if unbalanced(line): 
      print line 
+0

所以如果右括号会出现在字符串块内,它会算作错误。 – Odomontois 2012-08-09 19:53:00

+2

@Odomontois:OP在问题的评论中指出,将它视为错误可能是正确的。 – jfs 2012-08-09 21:39:37

+0

是的,这是正确的,parens和报价不应该嵌套。 – zakparks31191 2012-08-10 12:06:59

1

正则表达式 - 如果你行不包含嵌套的括号,该解决方案是非常简单的:

for line in myFile: 
    if re.search(r"\([^\(\)]*($|\()", line): 
     #this line contains unbalanced parentheses. 

如果您正在使用嵌套语句的可能性工作,它变得有点复杂:

for line in myFile: 
    paren_stack = [] 
    for char in line: 
     if char == '(': 
      paren_stack.append(char) 
     elif char == ')': 
      if paren_stack: 
       paren_stack.pop() 
      else: 
       #this line contains unbalanced parentheses. 
5

它可能看起来像矫枉过正使用的解析器包,但它是相当快:

text = """\ 
SL ID=0X14429A0B TY=STANDARD OWN=0X429A03 EXT=22 SLTK=0X1C429A0B MP=0X684003F0 SUB=0X24400007 
RT ID=0X18429A19 TY=CALONSC OWN=0X14429A0B EXLP=0X14429A0C CMDS=(N:0X8429A04,C:0X14429A0B) SGCC=2 REL=1 DESC="AURANT YD-INDSTRY LD" ATIS=T 
RT ID=0X18429A1A TY=CALONSC OWN=0X14429A0B EXLP=0X14429A08 CMDS=(R:0X8429A04,N:0X8429A05,C:0X14429A0B) SGCC=2 REL=2 DESC="AURANT YD TO TRK.1" ATIS=T 
RT ID=0X18429A1B TY=CALONSC OWN=0X14429A0B EXLP=0X14429A0A CMDS=(R:0X8429A04,R:0X8429A05,C:0X14429A0B) SGCC=2 REL=3 DESC="AURANT YD TO TRK.2" ATIS=T 
SL ID=0X14429A0C TY=STANDARD OWN=0X429A03 EXT=24 SLTK=0X1C429A0B MP=0X684003F1 SUB=0X24400007 
RT ID=0X18429A1C TY=CALONSC OWN=0X14429A0C EXLP=0X14429A0B CMDS=(N:0X8429A04,C:0X14429A0C) SGCC=2 REL=1 DESC="AURANT YD-INDSTRY LD" ATIS=T 
TK ID=0X1C429A08 TY=BLKTK OWN=0X429A03 EXT=12 LRMP=0X6C40BDAF LEN=5837 FSPD=60 PSPD=65 QUAL=TRK.1 MAXGE=0 MAXGW=0 JAL=4 ALT=12 SUB=0X24400007 RULES=(CTC:B:UP:0X24400007:485.7305:486.8359:T) LLON=-118.1766772 RLON=-118.1620059 LLAT=34.06838375 RLAT=34.07811764 LELE=416.6983 RELE=425.0596 ULAD=NO URAD=NO 
PT ID=0X20429A0F TY=STANDARD OWN=0X1C429A08 LTK=0X1C40006C RTK=0X1C429A0C REL=1 LEN=1 LQUAL="TRK.1" RQUAL="TRK.1" 
PTK OWN=0X1C429A08 PID=0X1C429A13 GOOD 
PTK OWN=0X1C429A(08 PID=0X1C429A13 BAD 
PTK OWN=0X1C429A08)PID=0X1C429A13 BAD 
PTK OWN=0X1C(42(9A))08 PID=0X1C429A13 GOOD 
PTK OWN=0X1C(42(9A))08 PID=0X1C42(9A13 BAD 
PTK OWN=0X1C(42(9A))08 PID=0X1C42"("9A13 GOOD 
""" 

from pyparsing import nestedExpr, quotedString 

paired_exprs = nestedExpr('(',')') | quotedString 

for i, line in enumerate(text.splitlines(), start=1): 
    # use pyparsing expression to strip out properly nested quotes/parentheses 
    stripped_line = paired_exprs.suppress().transformString(line) 

    # if there are any quotes or parentheses left, they were not 
    # properly nested 
    if any(unwanted in stripped_line for unwanted in '()"\''): 
     print i, ':', line 

打印:

10 : PTK OWN=0X1C429A(08 PID=0X1C429A13 BAD 
11 : PTK OWN=0X1C429A08)PID=0X1C429A13 BAD 
13 : PTK OWN=0X1C(42(9A))08 PID=0X1C42(9A13 BAD 
+1

'pyparsing'似乎取决于任务。我找不到反例。它正确地将'“(”()'视为平衡表达式,将'(“)”“视为不平衡表达式。'nested_expr()'默认情况下'ignoreExpr = quotedString'很方便, “”'也作为平衡的,即它处理引号字符串的转义,尽管'(\()'被视为不平衡。它不能识别(因为它可能应该在这种情况下)Unicode标点符号,例如'❩'作为平衡处理 – jfs 2012-08-09 21:15:49

+1

像这个解决方案最好的一个长镜头。谢谢你介绍我pyparsing :) – 2012-08-10 08:17:32

0

检查这个代码

from tokenize import * 
def syntaxCheck(line): 
    def readline(): 
     yield line 
     yield '' 
    par,quo,dquo = 0,0,0 
    count = { '(': (1,0,0),')': (-1,0,0),"'": (0,1,0),'"':(0,0,1) } 
    for countPar, countQuo,countDQuo in (
     count.get(token,(0,0))+(token,) for _,token,_,_,_ in tokenize(readline().__next__)): 
     par += countPar 
     quo ^= countQuo 
     dquo ^= countDQuo 
    return par,quo,dquo 

注意,括号内关闭报价犯规算,因为它算作一个字符串标记。

-1

那么我的解决方案可能不像花哨,但我说你只需要计算括号和引号的数量。如果它不是偶数,你就知道你错过了一些东西!