これがあなたが探していたものです、ジョン・マチン:私たちのシリアルの続編。今回は脳が正しい位置にあることを確認し、問題について考え続けました。
これで、デモコードが拡張されました。さて、あなたのいくつかの例示的なテキストで、文字列メソッドが十分であると私には明らかであり、私はその理由を理解しています。私はプロセスの根底を知り、肯定の具体的な理由を理解することに非常に興味があります。
次に、XMLの仕様をこれまで以上に研究し、W3cのバリデーターを使用してテストを実行して、XMLテキストの構造の詳細についての理解を深めました。それはかなり厳しい職業ですが、興味深いです。XMLの形式は、非常に厳格なルールとデボネアの自由が混在していることがわかりました。
例でコードを細かく分割するために使用したトリックから、XML形式ではテキストを行に分割する必要はないと結論付けます。実際、W3cのバリデーターが示したように、文字\n
、\r
および\t
は、構造の規則に違反しない限り、XMLテキストの多くの位置に配置できます。
たとえば、タグ間の制限なしに許可されます。その結果、要素が複数行を占める場合があります。また、タグでさえ\t
、1つのタグの名前の後にある場合は、複数の行に分割したり、複数の表に分割したりできます。XMLテキストの行は、私がいつも見ていたようにインデントする必要はありません。今では、読みやすさと論理的な理解のために選択されたのは個人的な便宜にすぎないことを理解しています。
さて、あなたは私よりもすべてをよく知っています、ジョン・マチン。おかげさまで、XML形式の複雑さに気づき、特殊なパーサー以外の方法で解析を非現実的にする理由をよりよく理解できました。ちなみに、一般的なコーダーは、このXML形式の厄介さ、つまり\n
XMLテキストのあちこちに文字が存在する可能性を認識しているのではないかと思います。
。
とにかく、私はこの概念的な沸騰ポットにしばらくいるので、有益な遊びとして、あなたのwhac_moles、ジョン・マチンの解決策を探し続けました。
文字列メソッドはゲームから外れているので、正規表現を磨きました。
私は知っています、私は知っています:あなたは私にXMLテキストの分析は正規表現を使ってもできないと言うでしょう。理由がよくわかったので、同意します。しかし、私はXMLテキストを解析するふりをしません。私の正規表現はXMLツリーのどの部分も抽出せず、テキストのごく一部のみを検索します。OPからの問題については、正規表現の使用は異端ではないと思います。
。
最初から、ルートの終了タグは属性がなく、ルートの開始タグよりも周囲の「ノイズ」が少ないため、ルートの終了タグを検索する方が簡単で自然だと思います。
だから私の解決策は今です:
~~XMLファイルを開きます
~~ファイルのポインタを最後から-200の位置に移動します
~~ファイルの最後の200文字を読みます
~~ここで、2つの戦略:
- コメントのみを削除してから、文字\ n、\ r、\tを考慮して正規表現でタグを検索します
- または、より単純な正規表現でタグを検索する前に、コメントとすべての文字\ n、\ r、\tを削除します
ファイルが大きいほど、解析または反復解析の使用と比較して、このアルゴリズムの速度が速くなります。私は以下のコードのすべての結果を書き、調べました。最初の戦略はより速いものです。
# coding: ascii
import xml.etree.cElementTree as et
# import xml.etree.ElementTree as et
# import lxml.etree as et
from cStringIO import StringIO
import re,urllib
xml5 = """\
<?xml version="1.0" ?>
<!-- this is a comment -->
<root\t
\r\t\r \r
><foo
>bar</foo\t \r></root
>
"""
xml6 = """\
<?xml version="1.0" ?>
<!-- this is a comment -->
<root
><foo
>bar</foo\n\t \t></root \t
\r>
<!-- \r \t
That's all, folks!
\t-->
"""
xml7 = '''<?xml version="1.0" ?>
<!-- <mole1> -->
<root><foo
\t\t\r\r\t/></root \t
>
<!-- </mole2>\t \r
\r-->
<!---->
'''
xml8 = '''<?xml version="1.0" ?><!-- \r<mole1> --><root> \t\t<foo \t\r\r/></root>\t<!-- </mole2> -->'''
sock = urllib.urlopen('http://www.cafeconleche.org/books/bible/examples/18/18-4.xsl')
xml9 = sock.read()
sock.close()
def rp(x):
return '\\r' if x.group()=='\r' else '\\t'
for xml_text in (xml5, xml6, xml7, xml8, xml9):
print '\\n\n'.join(re.sub('\r|\t',rp,xml_text).split('\n'))
print '-----------------------------'
xml_text_noc = re.sub('<!--.*?-->|[\n\r\t]','', xml_text,flags=re.DOTALL)
RE11 = '(?<=</)[^ >]+(?= *>)(?!.*</[^>]+>)' # with assertions # ^
m = re.search(RE11, xml_text_noc,re.DOTALL)
print "*** eyquem 11: " + repr(m.group() if m else "FAIL")
xml_text_noc = re.sub('<!--.*?-->|[\n\r\t]','', xml_text,flags=re.DOTALL)
RE12 = '</([^ >]+) *>(?!.*</[^>]+>)' # with group(1) # ^
m = re.search(RE12, xml_text_noc,re.DOTALL)
print "*** eyquem 12: " + repr(m.group(1) if m else "FAIL")
xml_text_noc = re.sub('<!--.*?-->|[\n\r\t]','', xml_text,flags=re.DOTALL)
RE13 = '</[^ >]+ *>(?!.*</[^>]+>)' # without group(1) # ^
m = re.search(RE13, xml_text_noc,re.DOTALL)
print "*** eyquem 13: " + repr(m.group()[2:-1].rstrip() if m else "FAIL")
xml_text_noc = re.sub('<!--.*?-->','', xml_text,flags=re.DOTALL)
RE14 = '(?<=</)[^ \n\r\t>]+(?=[ \n\r\t]*>)(?!.*</[^>]+>)' # with assertions # ^
m = re.search(RE14, xml_text_noc,re.DOTALL)
print "*** eyquem 14: " + repr(m.group() if m else "FAIL")
xml_text_noc = re.sub('<!--.*?-->','', xml_text,flags=re.DOTALL)
RE15 = '</([^ \n\r\t>]+)[ \n\r\t]*>(?!.*</[^>]+>)' # with group(1) # <
m = re.search(RE15, xml_text_noc,re.DOTALL)
print "*** eyquem 15: " + repr(m.group(1).rstrip() if m else "FAIL")
xml_text_noc = re.sub('<!--.*?-->','', xml_text,flags=re.DOTALL)
RE16 = '</[^ \n\r\t>]+[ \n\r\t]*>(?!.*</[^>]+>)' # without group(1) # <
m = re.search(RE16, xml_text_noc,re.DOTALL)
print "*** eyquem 16: " + repr(m.group()[2:-1].rstrip() if m else "FAIL")
print
filelike_obj = StringIO(xml_text)
tree = et.parse(filelike_obj)
print "*** parse: " + tree.getroot().tag
filelike_obj = StringIO(xml_text)
for event, elem in et.iterparse(filelike_obj, ('start', 'end')):
print "*** iterparse: " + elem.tag
break
print '\n============================================='
結果
<?xml version="1.0" ?> \n
<!-- this is a comment --> \n
<root\t\n
\r\t\r \r\n
><foo\n
\n
>bar</foo\t \r></root\n
>\n
-----------------------------
*** eyquem 11: 'root'
*** eyquem 12: 'root'
*** eyquem 13: 'root'
*** eyquem 14: 'root'
*** eyquem 15: 'root'
*** eyquem 16: 'root'
*** parse: root
*** iterparse: root
=============================================
<?xml version="1.0" ?> \n
<!-- this is a comment --> \n
<root\n
><foo\n
>bar</foo\n
\t \t></root \t\n
\r>\n
<!-- \r \t\n
That's all, folks!\n
\n
\t-->\n
-----------------------------
*** eyquem 11: 'root'
*** eyquem 12: 'root'
*** eyquem 13: 'root'
*** eyquem 14: 'root'
*** eyquem 15: 'root'
*** eyquem 16: 'root'
*** parse: root
*** iterparse: root
=============================================
<?xml version="1.0" ?>\n
<!-- <mole1> --> \n
<root><foo\n
\n
\t\t\r\r\t/></root \t\n
> \n
<!-- </mole2>\t\n
-->\n
<!---->\n
-----------------------------
*** eyquem 11: 'root'
*** eyquem 12: 'root'
*** eyquem 13: 'root'
*** eyquem 14: 'root'
*** eyquem 15: 'root'
*** eyquem 16: 'root'
*** parse: root
*** iterparse: root
=============================================
<?xml version="1.0" ?><!-- \r<mole1> --><root> \t\t<foo \t\r\r/></root>\t<!-- </mole2> -->
-----------------------------
*** eyquem 11: 'root'
*** eyquem 12: 'root'
*** eyquem 13: 'root'
*** eyquem 14: 'root'
*** eyquem 15: 'root'
*** eyquem 16: 'root'
*** parse: root
*** iterparse: root
=============================================
<?xml version="1.0"?>\r\n
<stylesheet\r\n
xmlns="http://www.w3.org/XSL/Transform/1.0"\r\n
xmlns:fo="http://www.w3.org/XSL/Format/1.0"\r\n
result-ns="fo">\r\n
\r\n
<template match="/">\r\n
<fo:root xmlns:fo="http://www.w3.org/XSL/Format/1.0">\r\n
\r\n
<fo:layout-master-set>\r\n
<fo:simple-page-master page-master-name="only">\r\n
<fo:region-body/>\r\n
</fo:simple-page-master>\r\n
</fo:layout-master-set>\r\n
\r\n
<fo:page-sequence>\r\n
\r\n
<fo:sequence-specification>\r\n
<fo:sequence-specifier-single page-master-name="only"/>\r\n
</fo:sequence-specification>\r\n
\r\n
<fo:flow>\r\n
<apply-templates select="//ATOM"/>\r\n
</fo:flow>\r\n
\r\n
</fo:page-sequence>\r\n
\r\n
</fo:root>\r\n
</template>\r\n
\r\n
<template match="ATOM">\r\n
<fo:block font-size="20pt" font-family="serif">\r\n
<value-of select="NAME"/>\r\n
</fo:block>\r\n
</template>\r\n
\r\n
</stylesheet>\r\n
-----------------------------
*** eyquem 11: 'stylesheet'
*** eyquem 12: 'stylesheet'
*** eyquem 13: 'stylesheet'
*** eyquem 14: 'stylesheet'
*** eyquem 15: 'stylesheet'
*** eyquem 16: 'stylesheet'
*** parse: {http://www.w3.org/XSL/Transform/1.0}stylesheet
*** iterparse: {http://www.w3.org/XSL/Transform/1.0}stylesheet
=============================================
このコードは、実行時間を測定するようになりました。
# coding: ascii
import xml.etree.cElementTree as et
# import xml.etree.ElementTree as et
# import lxml.etree as et
from cStringIO import StringIO
import re
import urllib
from time import clock
sock = urllib.urlopen('http://www.cafeconleche.org/books/bible/examples/18/18-4.xsl')
ch = sock.read()
sock.close()
# the following lines are intended to insert additional lines
# into the XML text before its recording in a file, in order to
# obtain a real file to use, containing an XML text
# long enough to observe easily the timing's differences
li = ch.splitlines(True)[0:6] + 30*ch.splitlines(True)[6:-2] + ch.splitlines(True)[-2:]
with open('xml_example.xml','w') as f:
f.write(''.join(li))
print 'length of XML text in a file : ',len(''.join(li)),'\n'
# timings
P,I,A,B,C,D,E,F = [],[],[],[],[],[],[],[],
n = 50
for cnt in xrange(50):
te = clock()
for i in xrange (n):
with open('xml_example.xml') as filelike_obj:
tree = et.parse(filelike_obj)
res_parse = tree.getroot().tag
P.append( clock()-te)
te = clock()
for i in xrange (n):
with open('xml_example.xml') as filelike_obj:
for event, elem in et.iterparse(filelike_obj, ('start', 'end')):
res_iterparse = elem.tag
break
I.append( clock()-te)
RE11 = '(?<=</)[^ >]+(?= *>)(?!.*</[^>]+>)' # with assertions # ^
te = clock()
for i in xrange (n):
with open('xml_example.xml') as f:
f.seek(-200,2)
xml_text = f.read()
xml_text_noc = re.sub('(<!--.*?-->|[\n\r\t])','', xml_text,flags=re.DOTALL)
m = re.search(RE11, xml_text_noc,re.DOTALL)
res_eyq11 = m.group() if m else "FAIL"
A.append( clock()-te)
RE12 = '</([^ >]+) *>(?!.*</[^>]+>)' # with group(1) # ^
te = clock()
for i in xrange (n):
with open('xml_example.xml') as f:
f.seek(-200,2)
xml_text = f.read()
xml_text_noc = re.sub('(<!--.*?-->|[\n\r\t])','', xml_text,flags=re.DOTALL)
m = re.search(RE12, xml_text_noc,re.DOTALL)
res_eyq12 = m.group(1) if m else "FAIL"
B.append( clock()-te)
RE13 = '</[^ >]+ *>(?!.*</[^>]+>)' # without group(1) # ^
te = clock()
for i in xrange (n):
with open('xml_example.xml') as f:
f.seek(-200,2)
xml_text = f.read()
xml_text_noc = re.sub('(<!--.*?-->|[\n\r\t])','', xml_text,flags=re.DOTALL)
m = re.search(RE13, xml_text_noc,re.DOTALL)
res_eyq13 = m.group()[2:-1] if m else "FAIL"
C.append( clock()-te)
RE14 = '(?<=</)[^ \n\r\t>]+(?=[ \n\r\t]*>)(?!.*</[^>]+>)' # with assertions # ^
te = clock()
for i in xrange (n):
with open('xml_example.xml') as f:
f.seek(-200,2)
xml_text = f.read()
xml_text_noc = re.sub('<!--.*?-->','', xml_text,flags=re.DOTALL)
m = re.search(RE14, xml_text_noc,re.DOTALL)
res_eyq14 = m.group() if m else "FAIL"
D.append( clock()-te)
RE15 = '</([^ \n\r\t>]+)[ \n\r\t]*>(?!.*</[^>]+>)' # with group(1) # <
te = clock()
for i in xrange (n):
with open('xml_example.xml') as f:
f.seek(-200,2)
xml_text = f.read()
xml_text_noc = re.sub('<!--.*?-->','', xml_text,flags=re.DOTALL)
m = re.search(RE15, xml_text_noc,re.DOTALL)
res_eyq15 = m.group(1) if m else "FAIL"
E.append( clock()-te)
RE16 = '</[^ \n\r\t>]+[ \n\r\t]*>(?!.*</[^>]+>)' # without group(1) # <
te = clock()
for i in xrange (n):
with open('xml_example.xml') as f:
f.seek(-200,2)
xml_text = f.read()
xml_text_noc = re.sub('<!--.*?-->','', xml_text,flags=re.DOTALL)
m = re.search(RE16, xml_text_noc,re.DOTALL)
res_eyq16 = m.group()[2:-1].rstrip() if m else "FAIL"
F.append( clock()-te)
print "*** parse: " + res_parse, ' parse'
print "*** iterparse: " + res_iterparse, ' iterparse'
print
print "*** eyquem 11: " + repr(res_eyq11)
print "*** eyquem 12: " + repr(res_eyq12)
print "*** eyquem 13: " + repr(res_eyq13)
print "*** eyquem 14: " + repr(res_eyq14)
print "*** eyquem 15: " + repr(res_eyq15)
print "*** eyquem 16: " + repr(res_eyq16)
print
print str(min(P))
print str(min(I))
print
print '\n'.join(str(u) for u in map(min,(A,B,C)))
print
print '\n'.join(str(u) for u in map(min,(D,E,F)))
結果:
length of XML text in a file : 22548
*** parse: {http://www.w3.org/XSL/Transform/1.0}stylesheet parse
*** iterparse: {http://www.w3.org/XSL/Transform/1.0}stylesheet iterparse
*** eyquem 11: 'stylesheet'
*** eyquem 12: 'stylesheet'
*** eyquem 13: 'stylesheet'
*** eyquem 14: 'stylesheet'
*** eyquem 15: 'stylesheet'
*** eyquem 16: 'stylesheet'
0.220554691169
0.172240771802
0.0273236743636
0.0266525536625
0.0265308269626
0.0246300539733
0.0241203758299
0.0238024015203
。
。
洗練されていないニーズを考えると、Aerealは\r
\n
\t
、名前だけでなく、可能な文字を含むルートのエンドタグを気にしないと思います。したがって、私の意見では、あなたにとって最善の解決策は次のとおりです。
def get_root_tag_from_xml_file(xml_file_path):
with open(xml_file_path) as f:
try: f.seek(-200,2)
except: f.seek(0,0)
finally: xml_text_noc = re.sub('<!--.*?-->','', f.read(), flags= re.DOTALL)
try:
return re.search('</[^>]+>(?!.*</[^>]+>)' , xml_text_noc, re.DOTALL).group()
except :
return 'FAIL'
John Machinの専門知識のおかげで、このソリューションは以前のソリューションよりも信頼性の高い仕事をします。さらに、それが表現されたように、それは要求に正確に答えます:それが暗黙のうちに狙われたので、構文解析なし、したがってより速い方法。
。
John Machin、このソリューションを無効にするXML形式の新しいトリッキーな機能を見つけますか?