XML文件简介
在处理从文本文件中读取数据的任务时,用户承担着至关重要的责任,即需要充分理解和明确指定在创建该文件时所遵循的一系列约定和规范。这些约定涵盖了多个方面,包括但不限于:
- 注释字符:识别并忽略文件中用于添加说明性文字的特殊字符或符号。
- 标题行的存在与否:确定文件的首行是否作为列名或仅作为数据的一部分。
- 值分隔符:明确字段之间是如何分隔的,比如逗号、制表符或其他特定字符。
- 缺失值的表示方法:规定在数据中如何标识缺失或未提供的值,可能采用特定的字符串(如“
N/A
”)、空字段或其他占位符。
为了更有效地管理和传达这些复杂的约定,可扩展标记语言(XML)
应运而生,它作为一种强大的工具,不仅能够处理标准数据集,还能轻松应对更为复杂的数据结构需求。XML凭借其高度的灵活性和可扩展性,正逐渐成为数据标记与交换的通用标准,广泛应用于各个领域,如地理数据(例如地图信息)、图形显示描述、数学表达式编码等。
XML的一大优势在于它提供了声明文件编码的机制,例如通过以下声明指定使用UTF-8编码:<?xml version="1.0" encoding="UTF-8"?>
。尽管这一声明并非强制性要求,但它对于确保数据的正确解析和跨平台兼容性至关重要。
在R语言环境中,处理XML文档的功能主要由XML
包提供,该包支持XML文件的读取与写入操作。此外,CRAN上的StatDataML
包是一个基于XML构建的实用示例,展示了如何在统计数据处理中应用XML标准。另一个值得注意的API是由xml2
包提供的,该包建立在高效的libxml2 C
库之上,为用户带来了更快速、更稳定的XML处理能力。
以下为XML文件的基础编写方法
XML文档声明:
XML文档声明位于XML文档的最前面,用于声明这是一个XML文档,并指定XML的版本和编码方式。语法如下:
<?xml version="1.0" encoding="UTF-8"?>
version
属性:用来表示XML的版本号,目前常用的版本是1.0。encoding
属性:指定XML文档的字符编码方式,常用的编码方式有UTF-8和GB2312等。standalone
属性(可选):说明文档是否是独立的,yes表示文档没有依赖外部文件,no表示文档依赖于外部文件。
元素(标签/节点):
XML文档由元素(或称为标签、节点)组成,元素用于定义数据的结构。元素由开始标签和结束标签组成,标签名大小写敏感。
- 开始标签:以
<
开头,标签名紧跟其后,以>
结束。 - 结束标签:以
</
开头,后面跟上标签名,以>
结束。 - 自闭合标签:对于没有内容的元素,可以使用自闭合标签,语法为
<标签名/>
。
例如:
<book>
<title>XML基础教程</title>
<author>张三</author>
</book>
属性:
元素可以包含属性,属性用于提供关于元素的额外信息。属性必须放在开始标签中,并且多个属性之间用空格分隔。属性值必须用引号(单引号或双引号)括起来。
例如:
<person age="30">阿斌</person>
注释:
XML中的注释用于添加对XML内容的说明,注释内容不会被XML解析器解析。注释以<!--
开始,以-->
结束。
例如:
<!-- 这是一个注释 -->
CDATA区:
CDATA区用于包含那些不应被XML解析器解析的文本数据,如包含大量特殊字符(如<
、&
等)的文本。CDATA区以<![CDATA[
开始,以]]>
结束。
例如:
<![CDATA[这是一个包含<和&等特殊字符的文本。]]>
命名空间:
命名空间用于避免元素名称冲突,通过前缀来标识命名空间。命名空间声明通常放在根元素上,使用xmlns
属性来定义。
例如:
<ns:book xmlns:ns="http://www.gayboys.com">
<ns:title>舔狗基础教程</ns:title>
<ns:author>阿斌</ns:author>
</ns:book>
XML编写注意事项:
- XML文档有且仅有一个根元素,所有其他元素都必须是根元素的子元素。
- 元素名不能以数字或特殊字符开头,可以包含字母、数字、下划线和点号,但不能包含空格。
- 标签名大小写敏感。
- 属性值必须用引号括起来。
- 注释不能嵌套,也不能出现在XML声明之前。
- XML语法非常严格,任何微小的错误都可能导致解析失败。
除了XML之外,YAML也是一种广受欢迎的结构化文本数据系统,它特别强调文档的人类可读性。在R中,yaml
包为YAML格式的支持提供了便捷的途径,使得用户可以轻松地读取和写入YAML文件,进一步丰富了数据处理与交换的选项。
本次使用的XML文件如下:
XML文件:
<?xml version="1.0" encoding="UTF-8"?>
<library>
<book>
<title>欢乐 Gay Boys</title>
<author>GGBoy</author>
<genre>教科书</genre>
<price>114.514</price>
<publish_date>2024-09-01</publish_date>
<description>属于统计学专业学生的高级搞基指南</description>
</book>
<book>
<title>快乐R语言</title>
<author>GGGirl</author>
<genre>教科书</genre>
<price>6.66</price>
<publish_date>2023-12-16</publish_date>
<description>属于统计学专业学生的高级拉拉指南</description>
</book>
</library>
YAML文件简介
YAML是"YAML Ain’t Markup Language"(YAML不是一种标记语言)的递归缩写,但实际上它意味着“Yet Another Markup Language”(仍是一种标记语言)。YAML是一种用于数据序列化的基于文本的标记语言,其强调以数据为中心,旨在方便人类使用,因此被广泛认为是一种人性化的数据格式语言。以下是YAML的详细介绍:
YAML的特点
- 可读性高:YAML语法清晰,易于阅读和书写,特别适合表达或编辑数据结构、配置文件等。
- 简洁性:相比JSON,YAML的语法更简洁,更适合用于配置文件。
- 数据类型丰富:能够表示字符串、数字、数组(列表)、对象(映射)等多种数据类型,且支持注释和多行字符串。
- 缩进敏感:YAML使用缩进来表示层级关系,不允许使用制表符进行缩进,且缩进的空格数在同一层级内保持一致即可。
- 扩展性强:YAML易于与其他编程语言集成,支持数据类型的自动转换。
YAML的应用场景
- 配置文件:许多编程语言和框架采用YAML作为配置文件的格式,如Spring Boot、Ansible等。
- 数据交换:YAML常和JSON一起被用作应用程序之间的数据交换格式。
- API文档:YAML是API文档的标准格式之一,用于描述API的接口、参数、返回值等信息。
- 容器编排:在容器化部署和云原生领域,YAML文件用于描述Kubernetes等资源定义。
YAML的基本语法
-
对象/映射(Mapping):键值对集合,每个键值对占一行,使用冒号(:)分隔键和值,值紧跟冒号后,并且前面可以有空格。
user: admin password: 123456
-
数组/序列(Sequence):一组按照次序排列的值,每项前加短横线(-),与父元素直接可以缩进一个空格。
fruits: - apple - banana - cherry
-
注释:从#开始到行末的部分被视为注释,可以放在值的前面或后面,也可以在标量值的上方或下方编写。
# 这是一个注释 user: admin # 这也是注释
-
字符串:默认不加引号,当字符串包含特殊字符时,需使用双引号括起来。
greeting: "Hello, World!"
-
数值和布尔值:直接表示,不需要特殊符号。
age: 30 is_active: true
YAML的这些特点和语法规则使其成为一种非常灵活且易于使用的数据序列化格式,在多个领域都有广泛的应用。
这次使用的YAML文件内容如下:
library:
books:
- title: "R语言基础"
author: "Gambardella, Matthew"
genre: "Computer"
price: 44.95
publish_date: "2000-10-01"
description: "An in-depth look at creating applications with R."
- title: "R语言进阶"
author: "Ralls, Kim"
genre: "Computer"
price: 5.95
publish_date: "2000-12-16"
description: "A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world."
使用R语言对XML文件与YAML文件进行操作
在进行操作前先在交互页面下载这三个R包:
install.packages("XML") # 处理XML数据
install.packages("xml2") # 另一个处理XML数据的包,基于libxml2 C库
install.packages("yaml") # 处理YAML数据
在上述三个R包中,xml2
包的设计哲学是更侧重于读取和解析XML数据而不是修改XML数据,因此在实际应用中,如果需要频繁地修改XML文档,可能会需要考虑使用其他工具或语言。
R语言通常不适合处理大规模数据的操作,而其他数据分析系统在这方面表现的会更好些,因此官方文档建议:与其在R中复制功能,不如让另一个数据分析系统来完成数据操作工作。例如有团队指出,他们倾向于在SAS、SSAS中进行数据操作,然后使用R中的survival
包进行分析。
XML文件基础操作
在进行操作前先加载xml2
包或XML
包:
library(xml2)
读取XML文件
我这里导入的xml2
包,使用read_xml()
函数来读取XML文件:
# 读取XML文件
xml_data <- read_xml("books.xml")
print(xml_data)
使用XML包中的xmlParse()
函数操作代码如下:
xml_data <- xmlParse(file = "books.xml")
print(xml_data)
代码运行结果如下:
获取根节点
使用包中的xml_root()
函数:
root_node <- xml_root(xml_data)
print(root_node)
获取特定节点集
# 获取所有<book>节点
books <- xml_find_all(xml_doc, "//book")
print(books)
提取单个节点的信息
建立一个变量first_book
通过索引来实现从books.xml文档中找到第一本书籍的节点,接下来从first_book
节点中提取各个子节点的信息,如标题、作者、类型、价格、出版日期和描述。
代码中:xml_find_first
函数用于在first_book
节点下查找第一个匹配的子节点。
xml_text
函数则用于提取该子节点的文本内容。
这里价格(price)
通常是以文本形式存储的,需要使用as.numeric
函数将其转换为数值类型。
# 提取第一个<book>节点的所有子节点信息
first_book <- books[1]
title <- xml_text(xml_find_first(first_book, "title"))
author <- xml_text(xml_find_first(first_book, "author"))
genre <- xml_text(xml_find_first(first_book, "genre"))
price <- as.numeric(xml_text(xml_find_first(first_book, "price")))
publish_date <- xml_text(xml_find_first(first_book, "publish_date"))
description <- xml_text(xml_find_first(first_book, "description"))
# 打印提取的信息
cat("Title:", title, "\nAuthor:", author, "\nGenre:", genre, "\nPrice:", price, "\nPublish Date:", publish_date, "\nDescription:", description, "\n")
提取所有书籍的信息
这里使用for
循环来遍历书籍的列表信息:
# 初始化一个列表来存储所有书籍的信息
all_books_info <- list()
# 遍历所有<book>节点并提取信息
for (book in books) {
book_info <- list(
title = xml_text(xml_find_first(book, "title")),
author = xml_text(xml_find_first(book, "author")),
genre = xml_text(xml_find_first(book, "genre")),
price = as.numeric(xml_text(xml_find_first(book, "price"))),
publish_date = xml_text(xml_find_first(book, "publish_date")),
description = xml_text(xml_find_first(book, "description"))
)
all_books_info <- append(all_books_info, list(book_info))
}
# 打印所有书籍的信息
print(all_books_info)
添加新节点并保存到新文件
xml2
包本身不直接支持修改XML文档,可以通过创建新的XML节点和结构,然后将其写入到新的XML文件中来实现。
# 创建新的XML文档
new_xml_doc <- xml_new_document()
new_root <- xml_add_child(new_xml_doc, "library")
# 创建新<book>节点及其子节点
new_book <- xml_add_child(new_root, "book")
xml_add_child(new_book, "title", "数据科学导论")
xml_add_child(new_book, "author", "DataGuy")
xml_add_child(new_book, "genre", "教科书")
xml_add_child(new_book, "price", "59.99")
xml_add_child(new_book, "publish_date", "2024-12-01")
xml_add_child(new_book, "description", "数据科学的基础教程")
# 打印新的XML文档
print(new_xml_doc)
# 将新的XML文档保存到另一个文件
write_xml(new_xml_doc, "new_books.xml")
新建的XML文档内容如下:
YAML文件基础操作
加载yaml
包
library(yaml)
读取YAML文件
使用的read_yaml()
文件:
yaml_content <- read_yaml("books.yaml")
print(yaml_content)
代码运行后输出如下:
访问YAML文件中的内容
这里访问的第一个书目的标题和作者。
first_book_title <- yaml_content$library$books[[1]]$title
first_book_author <- yaml_content$library$books[[1]]$author
cat("Title:", first_book_title, "\nAuthor:", first_book_author, "\n")
添加一个新的条目到YAML文件中
new_book <- list(
title = "R语言数据分析",
author = "Doe, John",
genre = "Computer",
price = 39.95,
publish_date = "2021-05-21",
description = "A comprehensive guide to data analysis with R."
)
yaml_content$library$books <- c(yaml_content$library$books, list(new_book))
修改现有书目的信息
例如,修改第一个书目的价格。
yaml_content$library$books[[1]]$price <- 49.95
删除一个书目
例如,删除第二个书目。
yaml_content$library$books <- yaml_content$library$books[-2]
将修改后的内容写回YAML文件
write_yaml(yaml_content, "new_books.yaml")
将R对象转换为YAML字符串
例如,将第一个书目对象转换为YAML字符串。
first_book_yaml <- as.yaml(yaml_content$library$books[[1]])
cat(first_book_yaml)
从YAML字符串解析回R对象
parsed_book <- yaml.load(first_book_yaml)
print(parsed_book)
验证YAML文件的正确性
这个方法仅适用于读取和解析YAML文件
tryCatch({
yaml_content <- read_yaml("books.yaml")
cat("YAML file is valid.\n")
}, error = function(e) {
cat("YAML file is invalid: ", e$message, "\n")
})
xml2
包函数表格
这个表格中doc
是一个通过read_xml()
函数读取的XML文档对象.
函数名 | 作用 | 范例 |
---|---|---|
read_xml | 读取XML文档并返回一个xml_document对象 | doc <- read_xml("<root><child>Text</child></root>") |
xml_name | 获取XML节点的名称 | name <- xml_name(xml_children(doc)[[1]]) |
xml_text | 获取XML节点的文本内容 | text <- xml_text(xml_find_all(doc, "//child")) |
xml_attr | 获取XML节点的属性值 | attr <- xml_attr(xml_find_first(doc, "//child"), "attribute") |
xml_add_child | 向XML节点添加子节点 | new_child <- xml_add_child(doc, "new_child", "New Text") |
xml_set_attr | 设置XML节点的属性 | xml_set_attr(xml_find_first(doc, "//child"), "new_attr", "value") |
xml_remove | 移除XML节点或属性 | xml_remove(xml_find_first(doc, "//new_child")) |
xml_find_all | 查找所有匹配的XML节点 | nodes <- xml_find_all(doc, "//child") |
xml_find_first | 查找第一个匹配的XML节点 | node <- xml_find_first(doc, "//child") |
xml_children | 获取XML节点的子节点 | children <- xml_children(doc) |
xml_parent | 获取XML节点的父节点 | parent <- xml_parent(xml_find_first(doc, "//child")) |
xml_siblings | 获取XML节点的兄弟节点 | siblings <- xml_siblings(xml_find_first(doc, "//child")) |
xml_ns | 获取或设置XML文档的命名空间 | ns <- xml_ns(doc) |
xml_ns_strip | 移除XML文档中的所有命名空间声明 | doc_no_ns <- xml_ns_strip(doc) |
xml_parse | 已弃用,使用read_xml | - |
xml_serialize | 已弃用 | - |
YAML
包函数表格
函数名 | 作用 | 范例 |
---|---|---|
read_xml | 读取XML文档并返回一个xml_document对象 | doc <- read_xml("<root><child>Text</child></root>") |
xml_name | 获取XML节点的名称 | name <- xml_name(xml_children(doc)[[1]]) |
xml_text | 获取XML节点的文本内容 | text <- xml_text(xml_find_all(doc, "//child")) |
xml_attr | 获取XML节点的属性值 | attr <- xml_attr(xml_find_first(doc, "//child"), "attribute") |
xml_add_child | 向XML节点添加子节点 | new_child <- xml_add_child(doc, "new_child", "New Text") |