2012-08-09 113 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

那麼我的解決方案可能不像花哨,但我說你只需要計算括號和引號的數量。如果它不是偶數,你就知道你錯過了一些東西!