如何替换R中XML元素内的文本?

时间:2013-01-31 08:06:47

标签: xml r replace

我有一个输入xml文件。

cat sample.xml

<Text>
    &lt;p&gt;ABC &lt;/p&gt;
</Text>

R脚本

library(XML)
doc = xmlTreeParse("sample.xml", useInternal = TRUE)
top<-xmlRoot(doc)

sub("&lt;","<",top[[1]])

我如何解决上面的pblm?

错误消息:as.vector(x,“character”)出错:   不能强迫'externalptr'类型为'character'类型的向量

编辑:目的是对xml中的特定节点使用readHTMLTable()函数,该节点具有html表但是对于&gt;它具有xml标记(&gt;&lt;)。和&lt;首先需要重新定位,因为readHTMLTable函数无法处理xml标记。

3 个答案:

答案 0 :(得分:6)

现在回答你真正的问题:

包含编码表的sample.xml:

<Text>
&lt;table&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;32&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
</Text>

阅读:

> library(XML)
> doc = xmlTreeParse("sample.xml", useInternal = TRUE)
> top<-xmlRoot(doc)

转换为文字:

> table=xmlValue(top)
> table
[1] "\n<table>\n<tr><td>1</td><td>2</td></tr>\n<tr><td>2</td><td>8</td></tr>\n<tr><td>4</td><td>32</td></tr>\n</table>\n"

现已准备好向readHTMLTable提供信息。无需字符串转换:

> readHTMLTable(table)
$`NULL`
  V1 V2
1  1  2
2  2  8
3  4 32

Howzat?

答案 1 :(得分:5)

如果您的问题是要知道如何替换XML节点内容中的字符串,那么您可以使用您提供的sample.xml文件检查以下代码:

## Parse the XML file
doc <- xmlTreeParse("sample.xml", useInternal = TRUE)
## Select the nodes we want to update
nodes <- getNodeSet(doc, "//Text")
## For each node, apply gsub on the content of the node
lapply(nodes, function(n) {
  xmlValue(n) <- gsub("ABC","foobar",xmlValue(n))
})

哪个会给你:

R> doc
<?xml version="1.0"?>
<Text>
    &lt;p&gt;foobar &lt;/p&gt;
</Text>

在这里你可以看到“ABC”被“foobar”取代。

但是,如果你尝试使用你想要实现的替换代码(替换“&amp; lt;”wit“&lt;”),它显然不起作用:

doc <- xmlTreeParse("sample.xml", useInternal = TRUE)
nodes <- getNodeSet(doc, "//Text")
lapply(nodes, function(n) {
  xmlValue(n) <- gsub("&lt;","<",xmlValue(n))
})

会给你:

R> doc
<?xml version="1.0"?>
<Text>
    &lt;p&gt;ABC &lt;/p&gt;
</Text>

为什么?如果你正在使用XML文件,你应该知道一些字符,主要是&lt;,&gt;,&amp;并且“保留,因为它们是基本XML语法的一部分。因此,它们不能出现在节点的内容中,否则解析将失败。因此它们被实体取代,这是一种排序例如,“&lt;”编码为“&amp; lt;”,“&amp;”编码为“&amp; amp;”等。

所以在这里,节点的内容包含“&lt;”字符,已自动转换为他的实体“&amp; lt;”。您尝试对代码执行的操作是替换“&amp; lt;”回到“&lt;”,R很乐意为你做,但由于它是节点的文本内容,XML包将立即将其转换回“&amp; lt;”。

所以,如果你想要实现的是转换你的字符串“&amp; lt; p&amp; gt; ABC&amp; lt; / p&amp; gt;”对于新的XML节点“&lt; p&gt; ABC&lt; / p&gt;”,你不能这样做。解决方案是解析文本字符串,从中检测名称和节点(此处为“p”),使用xmlNode()创建新节点,为其指定文本内容“ABC”并替换字符串使用您刚刚创建的节点。

另一种快速而又脏的方法是首先替换文件中的所有实体而不解析XML。像这样:

txt <- readLines(file("sample.xml"))
txt <- gsub("&lt;", "<", txt)
txt <- gsub("&gt;", ">", txt)
writeLines(txt, file("sample2.xml"))
doc2 <- xmlTreeParse("sample2.xml", useInternal = TRUE)

给出了:

R> doc2
<?xml version="1.0"?>
<Text>
  <p>ABC </p>
</Text>

这很危险,因为如果有“真实”“&amp; lt;”您文件中的实体,解析将失败。

答案 2 :(得分:3)

Ge xmlValue节点的值并替换。在这里,我将用DEF取代ABC:

> top<-xmlRoot(doc)
> top
<Text>
    &lt;p&gt;ABC &lt;/p&gt;
</Text> 
> xmlValue(top)=sub("ABC","DEF",xmlValue(top))
> top
<Text>
    &lt;p&gt;DEF &lt;/p&gt;
</Text> 

我之所以不尝试更换&lt;是因为这些字符序列在某些时候被XML代码解释了:

> substr(xmlValue(top),6,6)=="<"
[1] TRUE

虽然我已尝试使用xmlTreeParse和其他XML包函数的一些选项,但我似乎无法阻止xmlValue解释它们......