原始字符串字面量
- 原始字符串字面量是 C++ 提供的一种字符串表示法,用来简化特殊字符的处理(例如换行符、反斜杠)和避免过多的转义字符。它的引入是为了让字符串更可读、更易于书写,尤其是涉及正则表达式、文件路径等场景。
- 语法
原始字符串字面量以 R"delimiter(raw_characters)delimiter" 的形式表示:
delimiter 是用户定义的分隔符,可以用任意字符,但不能包含空白字符或括号。
raw_characters 是原始字符串的内容,所有字符将被保留原样。
- 特点
- 支持多行字符串:原始字符串字面量可以直接包含换行符,而无需使用 \n。
- 无需转义字符:在常规字符串中,特殊字符(如换行符 \n 或双引号 ")需要用反斜杠 \ 转义。
- 灵活的分隔符:如果字符串中包含了 “)”,可以使用自定义分隔符避免冲突。
std::string raw_str = R"delimiter(This is a raw string with ")".)delimiter";
- 可嵌套引号:原始字符串字面量允许在字符串中直接包含双引号。
- 使用方法同普通字符串一样。
TinyXML2
- TinyXML2 是一个轻量级的 XML 解析库,专为简单和快速设计,适合嵌入式系统、小型应用或需要解析/生成 XML 的程序。TinyXML2 的核心功能包括 XML 的读取、修改和写入。
- 跨平台:纯 C++ 编写,可在 Windows、Linux、macOS 等平台上使用。
- 无需外部依赖:仅需单个头文件和源文件(tinyxml2.h 和 tinyxml2.cpp)。
1. XML文档操作
1.1 LoadFile(const char* filename)
功能:将 XML 文档保存到文件。
参数:filename 是文件路径。
返回值:保存成功返回 XML_SUCCESS,否则返回错误码。
XMLDocument doc;//表示整个 XML 文件,负责加载、解析、保存。
if (doc.LoadFile("example.xml") != XML_SUCCESS) {
std::cerr << "Failed to load XML file!" << std::endl;
}
1.2SaveFile(const char* filename)
功能:将 XML 文档保存到文件。
参数:filename 是文件路径。
返回值:保存成功返回 XML_SUCCESS,否则返回错误码。
if (doc.SaveFile("output.xml") != XML_SUCCESS) {
std::cerr << "Failed to save XML file!" << std::endl;
}
1.3RootElement()
功能:获取 XML 文档的根元素。
返回值:指向根元素的指针,如果没有根元素返回 nullptr。
XMLElement* root = doc.RootElement();
if (!root) {
std::cerr << "No root element found!" << std::endl;
} else {
std::cout << "Root element: " << root->Name() << std::endl;
}
1.4Parse(const char* xml)
功能:直接解析 XML 字符串。
参数:xml 是一个 XML 格式的字符串。
返回值:解析成功返回 XML_SUCCESS,否则返回错误码。
const char* xml = "<Root><Child>Text</Child></Root>";
if (doc.Parse(xml) != XML_SUCCESS) {
std::cerr << "Failed to parse XML string!" << std::endl;
}
示例
// 1. 基本解析
XMLDocument doc;
// 解析简单的XML字符串
const char* xml = "<Root><Child>Hello</Child></Root>";
if (doc.Parse(xml) == XML_SUCCESS) {
XMLElement* root = doc.FirstChildElement("Root");
XMLElement* child = root->FirstChildElement("Child");
const char* text = child->GetText(); // 获取 "Hello"
}
// 2. 解析带属性的XML
const char* xmlWithAttr = R"(
<Person id="1" name="John">
<Age>30</Age>
<Address city="New York">
<Street>Broadway</Street>
</Address>
</Person>
)";
if (doc.Parse(xmlWithAttr) == XML_SUCCESS) {
XMLElement* person = doc.FirstChildElement("Person");
const char* name = person->Attribute("name"); // 获取 "John"
XMLElement* address = person->FirstChildElement("Address");
const char* city = address->Attribute("city"); // 获取 "New York"
}
// 3. 解析列表数据
const char* xmlList = R"(
<Inventory>
<Item id="1" name="Apple" price="0.5"/>
<Item id="2" name="Banana" price="0.3"/>
<Item id="3" name="Orange" price="0.4"/>
</Inventory>
)";
class InventoryParser {
public:
struct Item {
int id;
string name;
double price;
};
static vector<Item> parseItems(const char* xml) {
vector<Item> items;
XMLDocument doc;
if (doc.Parse(xml) == XML_SUCCESS) {
XMLElement* inventory = doc.FirstChildElement("Inventory");
for (XMLElement* elem = inventory->FirstChildElement("Item");
elem != nullptr;
elem = elem->NextSiblingElement("Item")) {
Item item;
item.id = elem->IntAttribute("id");
item.name = elem->Attribute("name");
item.price = elem->DoubleAttribute("price");
items.push_back(item);
}
}
return items;
}
};
// 4. 解析嵌套结构
const char* xmlNested = R"(
<Company>
<Department name="Engineering">
<Employee id="1">
<Name>John Doe</Name>
<Position>Developer</Position>
<Skills>
<Skill>C++</Skill>
<Skill>Python</Skill>
</Skills>
</Employee>
</Department>
</Company>
)";
class CompanyParser {
public:
static void parseEmployee(XMLElement* empElement) {
string name = empElement->FirstChildElement("Name")->GetText();
string position = empElement->FirstChildElement("Position")->GetText();
vector<string> skills;
XMLElement* skillsElem = empElement->FirstChildElement("Skills");
for (XMLElement* skill = skillsElem->FirstChildElement("Skill");
skill != nullptr;
skill = skill->NextSiblingElement("Skill")) {
skills.push_back(skill->GetText());
}
}
};
// 5. 错误处理
class XMLParser {
public:
static bool parseWithErrorHandling(const char* xml) {
XMLDocument doc;
XMLError err = doc.Parse(xml);
switch (err) {
case XML_SUCCESS:
return true;
case XML_ERROR_FILE_NOT_FOUND:
cerr << "XML file not found!" << endl;
break;
case XML_ERROR_PARSING_ELEMENT:
cerr << "Error parsing element!" << endl;
break;
case XML_ERROR_PARSING_ATTRIBUTE:
cerr << "Error parsing attribute!" << endl;
break;
default:
cerr << "Unknown XML error!" << endl;
}
return false;
}
};
// 6. 配置文件解析
const char* configXml = R"(
<Config>
<Database>
<Host>localhost</Host>
<Port>3306</Port>
<Username>root</Username>
<Password>secret123</Password>
</Database>
<Server>
<Port>8080</Port>
<MaxConnections>100</MaxConnections>
</Server>
</Config>
)";
class ConfigParser {
public:
struct DBConfig {
string host;
int port;
string username;
string password;
};
struct ServerConfig {
int port;
int maxConnections;
};
static pair<DBConfig, ServerConfig> parseConfig(const char* xml) {
XMLDocument doc;
DBConfig db;
ServerConfig server;
if (doc.Parse(xml) == XML_SUCCESS) {
XMLElement* config = doc.FirstChildElement("Config");
// 解析数据库配置
XMLElement* dbElem = config->FirstChildElement("Database");
db.host = dbElem->FirstChildElement("Host")->GetText();
db.port = atoi(dbElem->FirstChildElement("Port")->GetText());
db.username = dbElem->FirstChildElement("Username")->GetText();
db.password = dbElem->FirstChildElement("Password")->GetText();
// 解析服务器配置
XMLElement* serverElem = config->FirstChildElement("Server");
server.port = atoi(serverElem->FirstChildElement("Port")->GetText());
server.maxConnections = atoi(serverElem->FirstChildElement("MaxConnections")->GetText());
}
return {db, server};
}
};
2.元素操作
2.1 FirstChildElement(const char* name = nullptr)
功能:获取元素的第一个子元素。如果 name 参数不为空,则返回名称匹配的第一个子元素。
参数:name 是可选参数,表示子元素的名称。
返回值:指向第一个子元素的指针,如果没有找到返回 nullptr。
XMLElement* firstChild = root->FirstChildElement();
if (firstChild) {
std::cout << "First child element: " << firstChild->Name() << std::endl;
}
2.2 NextSiblingElement(const char* name = nullptr)
功能:获取当前元素的下一个同级元素。如果 name 参数不为空,则返回名称匹配的下一个同级元素。
参数:name 是可选参数,表示同级元素的名称。
返回值:指向下一个同级元素的指针,如果没有找到返回 nullptr。
for (XMLElement* child = root->FirstChildElement(); child != nullptr; child = child->NextSiblingElement()) {
std::cout << "Sibling element: " << child->Name() << std::endl;
}
2.3 SetName(const char* name)
功能:设置元素的名称。
参数:name 是新名称的字符串。
element->SetName("NewName");
2.4 DeleteChild(XMLElement* child)
功能:删除当前元素的指定子元素。
参数:child 是需要删除的子元素指针。
root->DeleteChild(child);
3. 属性操作
3.1SetAttribute(const char* name, const char* value)
功能:设置元素的字符串属性。
参数:name 是属性名,value 是属性值。
element->SetAttribute("key", "value");
3.2 Attribute(const char* name)
功能:获取元素指定名称的属性值。
参数:name 是属性名。
返回值:指向属性值的字符串指针,如果属性不存在返回 nullptr。
const char* attrValue = element->Attribute("key");
if (attrValue) {
std::cout << "Attribute value: " << attrValue << std::endl;
}
3.3 DeleteAttribute(const char* name)
功能:删除元素指定名称的属性。
参数:name 是属性名。
element->DeleteAttribute("key");
4. 文本操作
4.1 SetText(const char* text)
功能:设置元素的文本内容。
参数:text 是字符串,表示要设置的文本内容。
element->SetText("This is text content");
4.2 GetText()
功能:获取元素的文本内容。
返回值:指向文本内容的字符串指针,如果没有文本内容返回 nullptr。
const char* text = element->GetText();
if (text) {
std::cout << "Text content: " << text << std::endl;
}
5. 新建元素和节点
5.1 NewElement(const char* name)
功能:创建一个新的元素。
参数:name 是新元素的名称。
返回值:指向新创建的元素的指针。
XMLElement* newElement = doc.NewElement("NewElement");
5.2 InsertFirstChild(XMLNode* child)
功能:将一个子元素插入到当前元素的最前面。
参数:child 是要插入的子元素指针。
root->InsertFirstChild(newElement);
5.3 InsertEndChild(XMLNode* child)
功能:将一个子元素插入到当前元素的末尾。
参数:child 是要插入的子元素指针。
root->InsertEndChild(newElement);
6. 错误处理
6.1 ErrorID()
功能:返回文档的错误代码。
if (doc.ErrorID() != XML_SUCCESS) {
std::cerr << "Error occurred: " << doc.ErrorID() << std::endl;
}
6.2ErrorStr()
功能:返回错误信息字符串。
if (doc.ErrorID() != XML_SUCCESS) {
std::cerr << "Error details: " << doc.ErrorStr() << std::endl;
}
附录:XML文件格式
XML的基本结构
- 声明部分
XML文件的开头是声明,表示XML的版本和编码格式
<?xml version="1.0" encoding="UTF-8"?>
- 根元素
XML文件必须有且只有一个根元素,所有其他内容都嵌套在根元素中
<root> <!-- 子元素 --> </root>
- 元素
元素是XML的基本组成单位,通常由开始标签和结束标签组成
<element>内容</element>
- 可以嵌套子元素。 - 可以携带属性
<element attribute="value">内容</element>
- 属性
属性是描述元素的附加信息,格式为key=“value”
<book id="1" author="Author Name">Book Title</book>
- 注释
注释用于添加说明性文字
<!-- 这是一个注释 -->
- 文本内容
<name>张三</name>
- 声明部分
XML规则
- 标签匹配
标签匹配:每个开始标签必须有对应的结束标签<name>张三</name>
单标签形式需要使用斜杠
- 区分大小写
XML区分大小写,例如和是不同的标签。
- 属性值必须使用引号:可以是单引号或双引号<person gender="male"></person>
转义字符
特殊字符 | 转义符 | 示例代码 | 输出效果 |
---|---|---|---|
& |
& |
<text>AT&T</text> |
<text>AT&T</text> |
< |
< |
<text>1 < 2</text> |
<text>1 < 2</text> |
> |
> |
<text>3 > 2</text> |
<text>3 > 2</text> |
' |
' |
<attribute value='John's book' /> |
<attribute value='John's book' /> |
" |
" |
<attribute value="He said "Hello"" /> |
<attribute value="He said "Hello"" /> |
命名空间
- 由来
- XML 中,标签名是自由定义的,不同的 XML 文档中可能会出现相同名称的标签。如果多个 XML 文件需要合并或扩展,很容易发生命名冲突。命名空间为标签和属性分配一个唯一的标识符,解决了冲突问题。
- 命名空间实际上是一个唯一的 URI(Uniform Resource Identifier),通常是一个 URL(但不需要真实存在)。它标识某一组标签属于特定的上下文。
- 语法
- 默认命名空间
<root xmlns="http://example.com/namespace">
<child>Content</child>
</root>
<!--作用范围:没有前缀的所有标签(<root> 和 <child>)都属于这个命名空间。-->
- 带前缀的命名空间
<root xmlns:ns="http://example.com/namespace">
<ns:child>Content</ns:child>
</root>
<!--xmlns:ns="http://example.com/namespace" 声明了带前缀的命名空间-->
<!--标签 <ns:child> 使用了 ns 前缀,表示它属于命名空间 http://example.com/namespace-->
<!--作用范围:只有使用了 ns: 前缀的标签。-->
- 解决的问题
<person xmlns:p="http://example.com/person">
<p:name>John</p:name>
</person>
<product xmlns:prod="http://example.com/product">
<prod:name>Smartphone</prod:name>
</product>
模式位置
简单的XML文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- 这是一个简单的 XML 文件,描述书店中的书籍信息 -->
<bookstore>
<book id="1">
<title>XML Basics</title>
<author>John Doe</author>
<price currency="USD">29.99</price>
</book>
<book id="2">
<title>Learning XML</title>
<author>Jane Smith</author>
<price currency="EUR">39.99</price>
</book>
</bookstore>
复杂的XML文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- 复杂 XML 文件,描述一个电子商务平台的订单信息 -->
<ecommerce xmlns="http://example.com/ecommerce" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://example.com/ecommerce ecommerce.xsd">
<order id="1001" status="shipped">
<customer>
<name>John Doe</name>
<email>john.doe@example.com</email>
<phone>+123456789</phone>
<address>
<street>123 Main St</street>
<city>Springfield</city>
<state>IL</state>
<zipcode>62704</zipcode>
<country>USA</country>
</address>
</customer>
<items>
<item id="p101">
<name>Smartphone</name>
<quantity>1</quantity>
<price currency="USD">699.99</price>
</item>
<item id="p102">
<name>Wireless Headphones</name>
<quantity>2</quantity>
<price currency="USD">59.99</price>
</item>
</items>
<payment>
<method>Credit Card</method>
<transaction_id>TX123456789</transaction_id>
<amount currency="USD">819.97</amount>
</payment>
<shipping>
<method>Express</method>
<tracking_number>EXP123456789</tracking_number>
<date>2024-11-25</date>
</shipping>
</order>
</ecommerce>