UE4数据解析及其字符编码
字符编码变换
用TinyXml加载一个xml文档时,由于xml文档是UTF-8编码的,而UE4默认使用的是根据文档的编码方式来加载,在操作过程中需要进行编码转换
了解
ASCII码
使用1个byte,表示出英文中所有需要的字符(缺点:非英文的字符都无法表示)
UNICODE
是一个字符集,规定了不同的字符对应于一个唯一的整数,平时所说的使用UNICODE编码其实说的是UFT16编码(顾名思义就是用16位来表示一个字符,有65536个字符),UTF8、UTF16和UFT32则是基于UNICODE字符集的三种编码方式
TCHAR
TCHAR类型就是通过宏对char和wchar_t的封装,将其中的操作进行了统一。可根据当前平台情况选择对应的类型。_T修饰的字符串常量同理,根据是否定义的UNICODE宏,分别表示"“或L”"
转换
char 转 TCHAR
TCHAR* outTchar = new TCHAR[iLength + 1];
const char *outTchar1 = displayNameNode->GetText();
MultiByteToWideChar(CP_UTF8, 0, outTchar1, strlen(outTchar1) + 1, outTchar, iLength);
TCHAR转char
int32 iLength = WideCharToMultiByte(CP_UTF8, 0, *_XmlPath, -1, NULL, 0, NULL, NULL);
char* path = new char[iLength + 1];
WideCharToMultiByte(CP_UTF8, 0, *_XmlPath, -1, path, iLength, NULL, NULL);
注意:使用wideChartoMultiByte函数时,需要包含stringappiset.h头文件,而使用此头文件有需要包含windows.h头文件
而在UE4中有一个封装好的宏用于char与TCHAR的转换
- TCHAR_TO_ANSI - 将引擎字符串(TCHAR*)转换为 ANSI 字符串。
- ANSI_TO_TCHAR - 将 ANSI 字符串转换为引擎字符串(TCHAR*)。
注意:这些宏声明的对象的生命周期非常短。它们将被用作函数的参数。无法将一个变量指定到转换后的字符串内容,因为对象将处于作用域之外,字符串将被释放。
传入的参数必须是一个固有字符串,因为参数被类型转换为指针。如传入的是 TCHAR 而非 TCHAR*,编译后运行时将出现崩溃。
用法:
SomeApi(TCHAR_TO_ANSI(SomeUnicodeString));
UTF8_TO_TCHAR(outTchar)
注意:
这个宏的声明周期很短所以需要调用完直接赋值
FString转为TCHAR *(TCHAR与FString基本都能自动隐式转换)
Const FString SceneName;
const TCHAR *hc = *SceneName;
1. 解析json文件
1.1 json数据格式
{
{
“Cultrue": "ZH"
},
{
"MusicVolume: 0.3
},
{
"SoundVolume": 0.3
},
{
"ReCordData": [
{
"0": "Defaule"
},
{
"1": "Default"
}
]
}
}
解析Json数据示例
// 包含Include"Json.h"
void SlAiJsonHandle::RecordDataJsonRead(FStingp& Culture,float& MusicVolume,float& SoundVolume,TArray<FSting>&RecordDataList)
{
RecordDataFileName = FString("RecordData.json") //要读取的文件名
Relativepath = FString("Res/ConfigData/"); //文件地址
FString JsonValue" //读取的保存数据
/**将路径指定的文件读取到一个FString类型的二进制的数组中;*/
LoadStringFroFromFile(RecordDataFileName,RelativePath,JsonValue);
TArray<TSharePtr<FJsonValue>> JsonParsed; //解析后保存的数组
/**读取为JsonReader格式*/
TShardRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(Jsonvalue);
/**解析数据*/
if(FJsonSerializer::Deserialize(JsonReader,JsonParsed)){
// 根据键值对去获取第一个数据
CulTure = JsonParsed[0]->AsObject(0)o->GetStringField(FString("Culture"));
//获取数组数据
TArray<TSharedPtr<FJsonValue>> RecordDataArray = JsonParsed[3]->AsObject()->GetArrayField(FString("RecordData"));
// 遍历数组数据
for(int i = 0; i < RecordDataArray.Num();++i){
Fstring RecordDataName = RecordDataArray[i]->AsObject()->GetStaringField(Fstring::FromInt(i)); //夺取数据
RecordDataList.Add(RecordDataName); // 保存数据
}
}else{
//解析不成功
UE_LOG(LogSimpleAPP,Warning,TEXT("Culture = %s"),*Culture);
}
}
解析XML文件
1.使用UE4自带的XmlParser库进行读写
XML数据
<?xml version="1.0" encoding="UTF-8"?>
<Setting> // 每个<>都是一个节点(node
<MaxFPS id="wz9"> //id是属性(attribute)
<Setres>1440x720w</Setres> //一个完整的节点是元素(element)
<ScreenPercentage>180</ScreenPercentage>
</MaxFPS>
</Setting>
解析
//1.模块中包含XmlParser
//2.包含头文件
#include "Runtime/XmlParser/Public/XmlParser.h"
#include "Runtime/XmlParser/Public/XmlFile.h"
#include "Runtime/XmlParser/Public/XmlNode.h"
#include "Runtime/XmlParser/Public/FastXml.h"
bool UReadXMLBPLibrary::ReadXML(FString& MaxFPS, FString& SetRes, FString& ScreenPercentage)
{
if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*(FPaths::GamePluginsDir()/TEXT("ReadXML/XmlFiles/Setting.xml")))) //判断是否有此文件
{
FXmlFile* file = new FXmlFile(FPaths::GamePluginsDir() + "ReadXML/XMLFiles/Setting.xml"); // 获取文件地址
FXmlNode* RootNode = file->GetRootNode(); //获取文件根节点
FXmlNode *MaxFPSNode = RootNode->FindChildNode("MaxFPS"); // 寻找数据子节点
MaxFPS = *MaxFPSNode->GetContent(); // 获取其内容
FXmlNode* SetresNode = RootNode->FindChildNode("Setres");
SetRes = *SetresNode->GetContent();
FXmlNode* ScreenpercentageNode = RootNode->FindChildNode("ScreenPercentage");
ScreenPercentage = *ScreenpercentageNode->GetContent();
return true;
}
else
{
return false;
}
}
2.使用C++常用的tinyxml库进行读写
在工作中详细的使用了tinyxml库进行了xml读写,所以详细的说明
tinyxml使用前的配置步骤
- 下载tinyxml库
- 将tinyxml.h、tinystr.h、tinystr.cpp、tinyxml.cpp、tinyxmlerror.cpp、tinyxmlparser.cpp代码拖进同一种
注意:在UE4种使用要讲thinyxml.cpp种#include "thinyxml.h"放在第一个引用,不然会报错
使用了解
节点:一种对文档结构的描述对象
元素:对文档某一个数据块的描述
文本是指没有孩子的节点
节点类可以转换成元素对象。
TiXmlElement * pElement = pNode->ToElement();
那什么时候需要转换成元素呢? 当你需要元素的一些属性值是就需要转换了。
元素访问孩子的函数:
FirstChildElement() 返回当前元素的孩子元素
NextSiblingElement() 返回当前元素的同级元素
节点访问节点孩子的函数:
返回当前节点的孩子节点: FirstChild()
返回当前节点的同级下一个节点: NextSibing()
元素访问和节点访问在一般的访问下没有区别,两者都可以访问的孩子,对于一些特殊的情况下才需要区分。比如你要访问属性时,就需要用元素来找到第一个属性值。
链接节点到上一节点
RootNode->LinkEndChild(SceneNode);
链接文本到节点上
MissionNodes->LinkEndChild(MissionsText);
对于遍历一个xml文档时,思路一般是这样的:
-
载入一个xml
-
获得根元素(不是根节点)
-
循环访问每一个根元素的子元素
-
对每一个子元素进行解析。
获取子元素的类型
循环遍历子元素的下一级元素 -
递归调用第四步
如果定位一个节点
唯一确定一个节点的方法是根据节点名,属性名,属性值
1 根据xml的遍历思想找到与给定节点名一样的节点
2 如果这个节点有属性并且属性名和值与给定的一致,说明找到了。
3 如果没有一致的,说明解析失败。
4 如果没有给定属性名和值,则可以默认只查找与节点名一致的节点。
指针的 new和释放。
TinyXml已经帮我们把指针分配的内存进行了管理,在析构函数中进行了处理,我们不需要处理new出来的指针
读取示例
#include "ReadXML.h"
#include "Engine/Engine.h"
#include <windows.h>
#include <stringapiset.h>
#include "Paths.h"
#include "PlatformFilemanager.h"
bool UReadXML::ReadUnitXMLFile(TArray<FString> &str,
{
// 工程的相对路径+文件名称
FString _XmlPath = FPaths::GameSourceDir() + "Battle.xml";
// 将TCHAR转换char 并转UTF-8编码
int32 iLength = WideCharToMultiByte(CP_UTF8, 0, *_XmlPath, -1, NULL, 0, NULL, NULL);
char* path = new char[iLength + 1];
WideCharToMultiByte(CP_UTF8, 0, *_XmlPath, -1, path, iLength, NULL, NULL);
TiXmlDocument *myDocument = new TiXmlDocument();
if (myDocument->LoadFile(path))
{
// 获取跟元素
TiXmlElement *RootElement = myDocument->RootElement();
// 遍历节点
TiXmlElement *lpMapNode = NULL;
for (lpMapNode = RootElement->FirstChildElement(); lpMapNode != NULL; lpMapNode = lpMapNode->NextSiblingElement())
{
GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, "read failed");
const char* outchar = lpMapNode->Attribute("id");
TCHAR* outTchar = new TCHAR[iLength + 1];
MultiByteToWideChar(CP_UTF8, 0, outchar, strlen(outchar) + 1, outTchar, iLength);
str.Push(outTchar);
}
}
else
{
return false;
}
return true;
}
#pragma once
#include "tinystr.h"
#include "tinyxml.h"
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "ReadXML.generated.h"
UCLASS()
class DCXVEHICLEDEMO_API UReadXML : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "YPFUNC")
static bool ReadUnitXMLFile(TArray<FString> &str, TArray<FString> &displayName, TArray<FString> &mission, TArray<UTexture2D*> &icon, TArray<FString> &inconPrefab, TArray<FString> &category, TArray<FString> &load, TArray<FString> &temIndex, TArray<FString> &valid, TArray<FString> &elements);
};
读入示例
bool UReadXML::SaveMapData(const FString SceneName,const FString Mission,const FString Scenes,const FString Missions)
{
TiXmlDocument *doc = new TiXmlDocument(); // 创建xml文档对象
// 创建描述
TiXmlDeclaration *pDeclaration = new TiXmlDeclaration("1.0", "UTF-8", "");
// 链接到文档
doc->LinkEndChild(pDeclaration);
// 创建根节点
TiXmlElement *RootNode = new TiXmlElement("Numbers");
doc->LinkEndChild(RootNode);
// 创建子节点
TiXmlElement *SceneNameNode = new TiXmlElement("SceneName");
RootNode->LinkEndChild(SceneNameNode); //将节点链接到根节点上
TiXmlText *SceneNameText = new TiXmlText(TCHAR_TO_UTF8(*SceneName)); // 创建Text内容
SceneNameNode->LinkEndChild(SceneNameText); // 链接内容到Node
//Mission
TiXmlElement *MissionNode = new TiXmlElement("Mission");
RootNode->LinkEndChild(MissionNode); //将节点链接到根节点上
TiXmlText *MissionText = new TiXmlText(TCHAR_TO_UTF8(*Mission)); // 创建Text内容
MissionNode->LinkEndChild(MissionText); // 链接内容到Node
// SceneNote
TiXmlElement *SceneNode = new TiXmlElement("SceneNote");
RootNode->LinkEndChild(SceneNode); //将节点链接到根节点上
TiXmlText *SceneText = new TiXmlText(TCHAR_TO_UTF8(*Scenes)); // 创建Text内容
SceneNode->LinkEndChild(SceneText); // 链接内容到Node
// SceneNote
TiXmlElement *MissionNodes = new TiXmlElement("MissionNode");
RootNode->LinkEndChild(MissionNodes); //将节点链接到根节点上
TiXmlText *MissionsText = new TiXmlText(TCHAR_TO_UTF8(*Missions)); // 创建Text内容
MissionNodes->LinkEndChild(MissionsText); // 链接内容到Node
// list
TiXmlElement *ListNode = new TiXmlElement("list");
RootNode->LinkEndChild(ListNode);
// num
TiXmlElement *NumNode = new TiXmlElement("num");
NumNode->SetAttribute("i", "0");
// id
TiXmlElement *idNode = new TiXmlElement("id");
NumNode->LinkEndChild(idNode);
TiXmlText *idText = new TiXmlText(TCHAR_TO_UTF8(*Missions)); // 创建Text内容
idNode->LinkEndChild(idText); // 链接内容到Node
FString XmlPath = FPaths::GameDir()+ "SaveMapData";
int32 iLength = WideCharToMultiByte(CP_UTF8, 0, *XmlPath, -1, NULL, 0, NULL, NULL);
char* path = new char[iLength + 1];
//path = _XmlPath;
WideCharToMultiByte(CP_UTF8, 0, *XmlPath, -1, path, iLength, NULL, NULL);
doc->SaveFile(path);
return true;
}
参考博客
XML使用:https://blog.csdn.net/lgstudyvc/article/details/77859919
TinyXml节点查找及修改:https://www.cnblogs.com/lyggqm/p/4565749.html