2009-12-03 62 views
1

我正在使用Python編寫正則表達式來替換XML節點的部分字符串。正則表達式替換爲XML節點

源字符串看起來像:

 
Hello 
REPLACE(str1) this is to replace 
REPLACE(str2) this is to replace 

而結果字符串應該是這樣的:

 
Hello 
<replace name="str1"> this is to replace </replace> 
<replace name="str2"> this is to replace </replace> 

誰能幫助我?

回答

5

是什麼讓你的問題非常棘手一點是要匹配多行字符串的內部。您需要使用re.MULTILINE標誌來完成這項工作。

然後,您需要匹配源字符串中的一些組,然後在最終輸出中使用這些組。這裏是代碼,可以解決您的問題:

import re 


s_pat = "^\s*REPLACE\(([^)]+)\)(.*)$" 
pat = re.compile(s_pat, re.MULTILINE) 

s_input = """\ 
Hello 
REPLACE(str1) this is to replace 
REPLACE(str2) this is to replace""" 


def mksub(m): 
    return '<replace name="%s">%s</replace>' % m.groups() 


s_output = re.sub(pat, mksub, s_input) 

唯一棘手的部分是正則表達式模式。我們來仔細看看。

^匹配字符串的開頭。使用re.MULTILINE時,這與多行字符串內行的開始匹配;換句話說,它恰好在字符串中的換行符後面匹配。

\s*與可選空白符合。

REPLACE與文字字符串「REPLACE」匹配。

\(文字串匹配 「(」。

(開始 「匹配組」。

[^)]裝置 「匹配任何字符,而是 」)「。

+手段」 匹配一個或更多的前面的模式。

)關閉「匹配組」。

\)文字串 「)」

(.*)是含有另一種匹配組 「*」 相匹配。

$匹配字符串的結尾。與re.MULTILINE,這匹配多行字符串內的一行的結尾;換句話說,它匹配字符串中的換行符。

.匹配任何字符,而*意味着匹配零個或多個前面的模式。因此.*匹配任何內容,直到行的末尾。我們的模式有兩個「匹配組」。當你運行re.sub()時,它會產生一個「匹配對象」,它將被傳遞給mksub()。匹配對象有一個方法,.groups(),它將匹配的子串作爲元組返回,並替換爲替換文本。

編輯:你實際上不需要使用替換函數。您可以將替換文本中的特殊字符串\1放入替換文本中,並將替換爲匹配組1的內容。(匹配組計數從1;特殊匹配組0對應於由模式匹配的整個字符串。)唯一的\1字符串的棘手部分是\是特殊的字符串。在一個普通的字符串中,要得到一個\,你需要在連續放置兩個反斜槓,如下所示:"\\1"但是你可以使用Python的「原始字符串」來方便地寫入替換模式。這樣做你會得到這樣的:

進口重新

s_pat = "^\s*REPLACE\(([^)]+)\)(.*)$" 
pat = re.compile(s_pat, re.MULTILINE) 

s_repl = r'<replace name="\1">\2</replace>' 

s_input = """\ 
Hello 
REPLACE(str1) this is to replace 
REPLACE(str2) this is to replace""" 


s_output = re.sub(pat, s_repl, s_input) 
0

也許這樣嗎?

import re 

mystr = """Hello 
REPLACE(str1) this is to replace 
REPLACE(str2) this is to replace""" 

prog = re.compile(r'REPLACE\((.*?)\)\s(.*)') 

for line in mystr.split("\n"): 
    print prog.sub(r'< replace name="\1" > \2',line) 
0

像這樣的東西應該工作:

import re,sys 

f = open(sys.argv[1], 'r') 
for i in f: 
    g = re.match(r'REPLACE\((.*)\)(.*)', i) 
    if g is None: 
     print i 
    else: 
     print '<replace name=\"%s\">%s</replace>' % (g.group(1),g.group(2)) 
f.close() 
0
import re 

a="""Hello 
REPLACE(str1) this is to replace 
REPLACE(str2) this is to replace""" 

regex = re.compile(r"^REPLACE\(([^)]+)\)\s+(.*)$", re.MULTILINE) 

b=re.sub(regex, r'< replace name="\1" > \2 </replace>', a) 

print b 

會做一個線替換。

1

這是一個使用pyparsing的解決方案。我知道你特別詢問了一個正則表達式解決方案,但是如果你的需求改變了,你可能會發現擴展一個pyparsing解析器更容易。或者pyparsing原型解決方案可能會讓您更深入地瞭解導致正則表達式或其他最終實現的問題。

src = """\ 
Hello 
REPLACE(str1) this is to replace 
REPLACE(str2) this is to replace 
""" 

from pyparsing import Suppress, Word, alphas, alphanums, restOfLine 

LPAR,RPAR = map(Suppress,"()") 
ident = Word(alphas, alphanums) 
replExpr = "REPLACE" + LPAR + ident("name") + RPAR + restOfLine("body") 
replExpr.setParseAction(
    lambda toks : '<replace name="%(name)s">%(body)s </replace>' % toks 
    ) 

print replExpr.transformString(src) 

在這種情況下,您可以創建與pyparsing匹配的表達,定義解析動作做的文本轉換,然後調用transformString通過輸入源進行掃描,以發現所有的比賽,應用解析對每個匹配採取行動,並返回結果輸出。解析動作與@ steveha解決方案中的mksub具有類似的功能。

除了解析動作之外,pyparsing還支持命名錶達式的各個元素 - 我使用「name」和「body」來標記感興趣的兩個部分,它們在re解決方案中表示爲組1和2你可以在重新名組,相應的重新會是什麼樣子:

s_pat = "^\s*REPLACE\((?P<name>[^)]+)\)(?P<body>.*)$" 

不幸的是,通過名字來訪問這些羣體,你必須調用重新匹配對象的group()方法,你可以」 t直接執行命名的字符串插值,就像在我的lambda分析操作中一樣。但是這是Python,對吧?我們可以換一個類可贖回,這將使我們的名字到組類似字典的訪問:

class CallableDict(object): 
    def __init__(self,fn): 
     self.fn = fn 
    def __getitem__(self,name): 
     return self.fn(name) 

def mksub(m):  
    return '<replace name="%(name)s">%(body)s</replace>' % CallableDict(m.group) 

s_output = re.sub(pat, mksub, s_input) 

使用CallableDict,在mksub串插現在可以調用m.group每個字段,通過使它看起來我們正在檢索字典的['name']和['body']元素。