在混合词的 XML 文本中插入标签
Insert tag in XML text for mixed words
我希望从代表命名实体的索引中注释 XML 中的文本:
glossary = ['Paris', 'Vincennes', 'France', 'Guy de Maupassant', 'Maurice Ravel']
我在 Stack 上找到了一个解决方案(@Brad Clements),可以在此处部分注释 XML:insert tags in ElementTree text
对于 XML : <TEI><teiHeader></teiHeader><body><p>Paris est la capitale de France et Vincennes n'est pas loin.</p><p> Guy de Maupassant et Maurice Ravel inaugurent une nouvelle statue à Paris.</p></body></TEI>
它适用于短于 1 的词,例如“Paris”、“Vincennes”或“France”,但不适用于索引中的其他词。
我当前的输出是:<?xml version="1.0"?> <TEI><teiHeader/><body><p><entity>Paris</entity> est la capitale de <entity>France</entity> et <entity>Vincennes</entity> n'est pas loin. </p><p>Guy de Maupassant et Maurice Ravel inaugurent une nouvelle statue à <entity>Paris.</entity> </p></body></TEI>
预期输出:<?xml version="1.0"?> <TEI><teiHeader/><body><p><entity>Paris</entity> est la capitale de <entity>France</entity> et <entity>Vincennes</entity> n'est pas loin. </p><p><entity>Guy de Maupassant</entity> et <entity>Maurice Ravel</entity> inaugurent une nouvelle statue à <entity>Paris.</entity> </p></body></TEI>
我尝试改编的代码(取自insert tags in ElementTree text):
from lxml import etree
import string
import itertools
stylesheet = etree.XML("""
<xsl:stylesheet version="1.0"
xmlns:btest="uri:bolder"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*">
<xsl:copy />
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name(.)}">
<xsl:copy-of select="@*" />
<xsl:apply-templates select="text()" />
<xsl:apply-templates select="./*" />
</xsl:element>
</xsl:template>
<xsl:template match="text()">
<xsl:copy-of select="btest:bolder(.)/node()" />
</xsl:template>
</xsl:stylesheet>
""")
glossary = ['Paris', 'Vincennes', 'France', 'Guy de Maupassant', 'Maurice Ravel']
def bolder(context, s):
results = []
r = None
# - Iteration over the index and the words contained in the sequence
for gw, word in itertools.zip_longest(glossary, s[0].split()):
# - Little preprocessing for words with punctuation
word_clean = word.translate(str.maketrans('', '', string.punctuation))
# 1) If word in sequence directly match with glossary (index) add tag
if word_clean in glossary:
if r is not None:
results.append(r)
r = etree.Element('r')
b = etree.SubElement(r, 'entity')
b.text = word
b.tail = ' '
results.append(r)
r = None
# 2) Otherwise take the word from the index and check that it is contained in the sequence
# (if the word is composed eg. First name + last name
# and that it is not None)
elif gw is not None and gw in s[0] and len(gw.split()) > 1:
# repeat the process to annotate
if r is not None:
results.append(r)
r = etree.Element('r')
b = etree.SubElement(r, 'entity')
b.text = gw
b.tail = ' '
results.append(r)
r = None
# 3) if none of the prerequisites, add text to output with no tag
else:
if r is None:
r = etree.Element('r')
r.text = '%s%s ' % (r.text or '', word)
if r is not None:
results.append(r)
return results
def test():
ns = etree.FunctionNamespace('uri:bolder')
ns['bolder'] = bolder
transform = etree.XSLT(stylesheet)
new = str(transform(etree.XML("""<TEI><teiHeader></teiHeader><body><p>Paris est la capitale de France et Vincennes n'est pas loin.</p><p> Guy de Maupassant et Maurice Ravel inaugurent une nouvelle statue à Paris.</p></body></TEI>""")))
print(new)
if __name__ == "__main__":
test()
但输出仍然不足(重复和遗漏):
<?xml version="1.0"?>
<TEI><teiHeader/><body><p><entity>Paris</entity> est la capitale de <entity>France</entity> et <entity>Vincennes</entity> n'est pas loin. </p><p>Guy de Maupassant <entity>Guy de Maupassant</entity> <entity>Maurice Ravel</entity> Ravel inaugurent une nouvelle statue à <entity>Paris.</entity> </p></body></TEI>
如何改进上述解决方案以正确匹配仅构成一个实体的混合词?提前致谢。美好的一天。
在 XSLT 3 中,您可以使用 analyze-string
:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
expand-text="yes"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="entity-strings" as="xs:string*" select="'Paris', 'Vincennes', 'France', 'Guy de Maupassant', 'Maurice Ravel'"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="text()">
<xsl:apply-templates select="analyze-string(., string-join($entity-strings, '|'))" mode="entity"/>
</xsl:template>
<xsl:template match="fn:match" mode="entity">
<entity>{.}</entity>
</xsl:template>
</xsl:stylesheet>
Saxonica 的 Saxon-C(任何版本)有一个 Python API,因此它可以与 Python 3.
一起使用
或者对 lxml 使用 Python 扩展函数作为
from lxml import etree as ET
import re
stylesheet = ET.XML("""
<xsl:stylesheet version="1.0"
xmlns:btest="uri:bolder"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:copy-of select="btest:bolder(.)/node()" />
</xsl:template>
</xsl:stylesheet>
""")
def bolder(context, s):
results = []
splits = re.split(r'(Paris|Vincennes|France|Guy de Maupassant|Maurice Ravel)', s[0])
for p, split in enumerate(splits):
if p % 2 == 0:
el = ET.Element("element")
el.text = split
else:
el = ET.Element("element")
entity = ET.SubElement(el, "entity")
entity.text = split
results.append(el)
return results
ns = ET.FunctionNamespace('uri:bolder')
ns['bolder'] = bolder
transform = ET.XSLT(stylesheet)
new = str(transform(ET.XML("""<TEI><teiHeader></teiHeader><body><p>Paris est la capitale de France et Vincennes n'est pas loin.</p><p> Guy de Maupassant et Maurice Ravel inaugurent une nouvelle statue à Paris.</p></body></TEI>""")))
print(new)
这是一个示例,说明如何在 XSLT 样式表中完成所有操作(假设使用 libxslt
处理器):
XSLT 1.0 + EXSLT str:tokenize()
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="str">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:param name="glossary">Paris|Vincennes|France|Guy de Maupassant|Maurice Ravel</xsl:param>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p/text()">
<xsl:call-template name="tag-terms">
<xsl:with-param name="string" select="."/>
<xsl:with-param name="terms" select="str:tokenize($glossary, '|')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="tag-terms">
<xsl:param name="string"/>
<xsl:param name="terms"/>
<xsl:choose>
<xsl:when test="$terms">
<xsl:variable name="term" select="$terms[1]" />
<xsl:choose>
<xsl:when test="contains($string, $term)">
<!-- process substring-before with the remaining terms -->
<xsl:call-template name="tag-terms">
<xsl:with-param name="string" select="substring-before($string, $term)"/>
<xsl:with-param name="terms" select="$terms[position() > 1]"/>
</xsl:call-template>
<!-- matched term -->
<entity>
<xsl:value-of select="$term"/>
</entity>
<!-- continue with substring-after -->
<xsl:call-template name="tag-terms">
<xsl:with-param name="string" select="substring-after($string, $term)"/>
<xsl:with-param name="terms" select="$terms"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- pass the entire string for processing with the remaining terms -->
<xsl:call-template name="tag-terms">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="terms" select="$terms[position() > 1]"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
应用于您的输入示例,这将 return:
结果
<?xml version="1.0" encoding="utf-8"?>
<TEI>
<teiHeader/>
<body>
<p><entity>Paris</entity> est la capitale de <entity>France</entity> et <entity>Vincennes</entity> n'est pas loin.</p>
<p> <entity>Guy de Maupassant</entity> et <entity>Maurice Ravel</entity> inaugurent une nouvelle statue à <entity>Paris</entity>.</p>
</body>
</TEI>
词汇表可以在运行时作为分隔字符串传递给样式表。
我希望从代表命名实体的索引中注释 XML 中的文本:
glossary = ['Paris', 'Vincennes', 'France', 'Guy de Maupassant', 'Maurice Ravel']
我在 Stack 上找到了一个解决方案(@Brad Clements),可以在此处部分注释 XML:insert tags in ElementTree text
对于 XML : <TEI><teiHeader></teiHeader><body><p>Paris est la capitale de France et Vincennes n'est pas loin.</p><p> Guy de Maupassant et Maurice Ravel inaugurent une nouvelle statue à Paris.</p></body></TEI>
它适用于短于 1 的词,例如“Paris”、“Vincennes”或“France”,但不适用于索引中的其他词。
我当前的输出是:<?xml version="1.0"?> <TEI><teiHeader/><body><p><entity>Paris</entity> est la capitale de <entity>France</entity> et <entity>Vincennes</entity> n'est pas loin. </p><p>Guy de Maupassant et Maurice Ravel inaugurent une nouvelle statue à <entity>Paris.</entity> </p></body></TEI>
预期输出:<?xml version="1.0"?> <TEI><teiHeader/><body><p><entity>Paris</entity> est la capitale de <entity>France</entity> et <entity>Vincennes</entity> n'est pas loin. </p><p><entity>Guy de Maupassant</entity> et <entity>Maurice Ravel</entity> inaugurent une nouvelle statue à <entity>Paris.</entity> </p></body></TEI>
我尝试改编的代码(取自insert tags in ElementTree text):
from lxml import etree
import string
import itertools
stylesheet = etree.XML("""
<xsl:stylesheet version="1.0"
xmlns:btest="uri:bolder"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*">
<xsl:copy />
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name(.)}">
<xsl:copy-of select="@*" />
<xsl:apply-templates select="text()" />
<xsl:apply-templates select="./*" />
</xsl:element>
</xsl:template>
<xsl:template match="text()">
<xsl:copy-of select="btest:bolder(.)/node()" />
</xsl:template>
</xsl:stylesheet>
""")
glossary = ['Paris', 'Vincennes', 'France', 'Guy de Maupassant', 'Maurice Ravel']
def bolder(context, s):
results = []
r = None
# - Iteration over the index and the words contained in the sequence
for gw, word in itertools.zip_longest(glossary, s[0].split()):
# - Little preprocessing for words with punctuation
word_clean = word.translate(str.maketrans('', '', string.punctuation))
# 1) If word in sequence directly match with glossary (index) add tag
if word_clean in glossary:
if r is not None:
results.append(r)
r = etree.Element('r')
b = etree.SubElement(r, 'entity')
b.text = word
b.tail = ' '
results.append(r)
r = None
# 2) Otherwise take the word from the index and check that it is contained in the sequence
# (if the word is composed eg. First name + last name
# and that it is not None)
elif gw is not None and gw in s[0] and len(gw.split()) > 1:
# repeat the process to annotate
if r is not None:
results.append(r)
r = etree.Element('r')
b = etree.SubElement(r, 'entity')
b.text = gw
b.tail = ' '
results.append(r)
r = None
# 3) if none of the prerequisites, add text to output with no tag
else:
if r is None:
r = etree.Element('r')
r.text = '%s%s ' % (r.text or '', word)
if r is not None:
results.append(r)
return results
def test():
ns = etree.FunctionNamespace('uri:bolder')
ns['bolder'] = bolder
transform = etree.XSLT(stylesheet)
new = str(transform(etree.XML("""<TEI><teiHeader></teiHeader><body><p>Paris est la capitale de France et Vincennes n'est pas loin.</p><p> Guy de Maupassant et Maurice Ravel inaugurent une nouvelle statue à Paris.</p></body></TEI>""")))
print(new)
if __name__ == "__main__":
test()
但输出仍然不足(重复和遗漏):
<?xml version="1.0"?>
<TEI><teiHeader/><body><p><entity>Paris</entity> est la capitale de <entity>France</entity> et <entity>Vincennes</entity> n'est pas loin. </p><p>Guy de Maupassant <entity>Guy de Maupassant</entity> <entity>Maurice Ravel</entity> Ravel inaugurent une nouvelle statue à <entity>Paris.</entity> </p></body></TEI>
如何改进上述解决方案以正确匹配仅构成一个实体的混合词?提前致谢。美好的一天。
在 XSLT 3 中,您可以使用 analyze-string
:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
expand-text="yes"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="entity-strings" as="xs:string*" select="'Paris', 'Vincennes', 'France', 'Guy de Maupassant', 'Maurice Ravel'"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="text()">
<xsl:apply-templates select="analyze-string(., string-join($entity-strings, '|'))" mode="entity"/>
</xsl:template>
<xsl:template match="fn:match" mode="entity">
<entity>{.}</entity>
</xsl:template>
</xsl:stylesheet>
Saxonica 的 Saxon-C(任何版本)有一个 Python API,因此它可以与 Python 3.
一起使用或者对 lxml 使用 Python 扩展函数作为
from lxml import etree as ET
import re
stylesheet = ET.XML("""
<xsl:stylesheet version="1.0"
xmlns:btest="uri:bolder"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:copy-of select="btest:bolder(.)/node()" />
</xsl:template>
</xsl:stylesheet>
""")
def bolder(context, s):
results = []
splits = re.split(r'(Paris|Vincennes|France|Guy de Maupassant|Maurice Ravel)', s[0])
for p, split in enumerate(splits):
if p % 2 == 0:
el = ET.Element("element")
el.text = split
else:
el = ET.Element("element")
entity = ET.SubElement(el, "entity")
entity.text = split
results.append(el)
return results
ns = ET.FunctionNamespace('uri:bolder')
ns['bolder'] = bolder
transform = ET.XSLT(stylesheet)
new = str(transform(ET.XML("""<TEI><teiHeader></teiHeader><body><p>Paris est la capitale de France et Vincennes n'est pas loin.</p><p> Guy de Maupassant et Maurice Ravel inaugurent une nouvelle statue à Paris.</p></body></TEI>""")))
print(new)
这是一个示例,说明如何在 XSLT 样式表中完成所有操作(假设使用 libxslt
处理器):
XSLT 1.0 + EXSLT str:tokenize()
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="str">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:param name="glossary">Paris|Vincennes|France|Guy de Maupassant|Maurice Ravel</xsl:param>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p/text()">
<xsl:call-template name="tag-terms">
<xsl:with-param name="string" select="."/>
<xsl:with-param name="terms" select="str:tokenize($glossary, '|')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="tag-terms">
<xsl:param name="string"/>
<xsl:param name="terms"/>
<xsl:choose>
<xsl:when test="$terms">
<xsl:variable name="term" select="$terms[1]" />
<xsl:choose>
<xsl:when test="contains($string, $term)">
<!-- process substring-before with the remaining terms -->
<xsl:call-template name="tag-terms">
<xsl:with-param name="string" select="substring-before($string, $term)"/>
<xsl:with-param name="terms" select="$terms[position() > 1]"/>
</xsl:call-template>
<!-- matched term -->
<entity>
<xsl:value-of select="$term"/>
</entity>
<!-- continue with substring-after -->
<xsl:call-template name="tag-terms">
<xsl:with-param name="string" select="substring-after($string, $term)"/>
<xsl:with-param name="terms" select="$terms"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- pass the entire string for processing with the remaining terms -->
<xsl:call-template name="tag-terms">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="terms" select="$terms[position() > 1]"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
应用于您的输入示例,这将 return:
结果
<?xml version="1.0" encoding="utf-8"?>
<TEI>
<teiHeader/>
<body>
<p><entity>Paris</entity> est la capitale de <entity>France</entity> et <entity>Vincennes</entity> n'est pas loin.</p>
<p> <entity>Guy de Maupassant</entity> et <entity>Maurice Ravel</entity> inaugurent une nouvelle statue à <entity>Paris</entity>.</p>
</body>
</TEI>
词汇表可以在运行时作为分隔字符串传递给样式表。