#c #parsing
Вопрос:
я пытаюсь проанализировать этот конфигурационный файл …
MODEL: "modelname1" { FILEPATH = "FILEPATH1"; TEXTUREPATH = "TEXTUREPATH1"; NORMALPATH = "NORMALPATH1"; }
MODEL:"modelname2"{FILEPATH = "FILEPATH2";TEXTUREPATH = "TEXTUREPATH2";NORMALPATH = "NORMALPATH2";}
вот моя попытка :
#include <iostream>
#include <map>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
struct ModelData
{
string tagName;
string filePath;
string texturePath;
string normalPath;
};
vector<ModelData> g_modelData;
void GetStringValue( string _source, string _tagName, stringamp; _outStrVal )
{
size_t equalPos = _source.find_first_of('=');
string tagName = _source.substr(0, equalPos);
tagName.erase(remove(tagName.begin(), tagName.end(), '"'), tagName.end());
if (tagName == _tagName)
{
_source = _source.substr(equalPos 1);
_source.erase(remove(_source.begin(), _source.end(), '"'), _source.end());
_outStrVal = _source;
}
}
int main()
{
ifstream infile("modeldata.txt", ios::in);
if (!infile.good())
{
cout << "Error opening file!" << endl;
}
string line;
line.resize(1024);
while (infile.getline((char*)line.data(), line.size(), 'n'))
{
line.erase(remove(line.begin(), line.end(), ' '), line.end());
line.erase(remove(line.begin(), line.end(), 't'), line.end());
size_t colonPos = line.find_first_of(':');
string tagStr = line.substr(0, colonPos);
if (tagStr == "MODEL")
{
ModelData md;
string tagValueStr = line.substr(colonPos 1);
size_t tagNamePos = tagValueStr.find_first_of('{');
string tagName = tagValueStr.substr(0, tagNamePos);
tagName.erase(remove(tagName.begin(), tagName.end(), '"'), tagName.end());
md.tagName = tagName;
tagValueStr = tagValueStr.substr(tagNamePos 1);
size_t tagValueTerminatingPos = tagValueStr.find_first_of('}');
tagValueStr = tagValueStr.substr(0, tagValueTerminatingPos);
string temp;
char context[1024];
memset( context,0, 1024 );
temp = strtok_s(amp;tagValueStr[0], ";", (char**)amp;context);
GetStringValue(temp, "FILEPATH", md.filePath);
temp = strtok_s(nullptr, ";", (char**)amp;context);
GetStringValue(temp, "TEXTUREPATH", md.texturePath);
temp = strtok_s(nullptr, ";", (char**)amp;context);
GetStringValue(temp, "NORMALPATH", md.normalPath);
g_modelData.push_back(md);
}
}
infile.close();
system("pause");
return 0;
}
но что, если я отформатирую файл конфигурации в
MODEL: "modelname1"
{
FILEPATH = "FILEPATH1";
TEXTUREPATH = "TEXTUREPATH1";
NORMALPATH = "NORMALPATH1";
}
MODEL:"modelname2"
{
FILEPATH = "FILEPATH2";
TEXTUREPATH = "TEXTUREPATH2";
NORMALPATH = "NORMALPATH2";
}
тогда getline не будет работать, и мне придется разбирать символы по символам …
поэтому я хотел спросить, что может быть более быстрой реализацией вышеуказанного изменения, я хочу извлечь эти данные внутри программы и использовать их для загрузки.
Я в порядке, если вы хотите изменить дизайн этого конфигурационного файла для лучшего потока.
ОБНОВЛЕНИЕ ЗАПРОСА:
Хотел бы узнать больше о том, как анализировать вложенные блоки и фигурные скобки, но не нашел многого в поиске Google. тогда мой конфигурационный файл должен был бы выглядеть так …
MODEL: "modelname1"
{
FILEPATH = "FILEPATH1";
TEXTUREPATH = "TEXTUREPATH1";
NORMALPATH = "NORMALPATH1";
PLACEHOLDER =
{
DATA0 = true//1
DATA1 = 1.0f, 1.0f, 1.0f;
DATA2 = 1.0f, 1.0f, 1.0f;
DATA3 = 1.0f, 1.0f, 1.0f;
}
}
следует прочитать это в
struct ModelData
{
string tagName;
string filePath;
string texturePath;
string normalPath;
bool Data0;
Vec3 Data1;
Vec3 Data2;
Vec3 Data3;
};
Комментарии:
1. Братан, я тоже принимаю реализацию c …
2. Два предложения: Используйте разработку на основе тестов, чтобы вы могли легко отслеживать свой прогресс и избегать регрессий во время разработки. Другое предложение-использовать существующий метаформат вместо того, чтобы изобретать свой собственный, например, INI, XML, YAML, JSON.
3. не хочу использовать зависимости и другие библиотеки.
4. Изучение того, как использовать библиотеки (проверенного!) кода, — хороший навык для изучения. Не тратьте энергию на изобретение собственных колес. Поэтому мне любопытно, почему вы не хотите использовать библиотеки?
5. речь идет об обсуждении и изучении основ … библиотеки могут легко выполнить эту работу, но оставляют вас очень зависимыми … так что, пожалуйста, извините за использование lib.
Ответ №1:
Поскольку вы хотите изучить основы, я отвечу только с набросками решений. Обратите внимание, что ни то, ни другое не будет работать, если вы хотите добавить вложенность блоков в свой язык. Для этого вам нужна правильная грамматика, подобная boost::spirit
или похожая.
Используется getline
для извлечения одного блока за раз:
std::string model_name, block_contents;
getline(infile, model_name, '{');
getline(infile, block_contents, '}');
Используйте регулярное выражение, чтобы сделать то же самое:
auto block_regex{ R"(MODELs*:s*"(.*?)"s*{(.*?)})" };
Затем вы можете применить a std::regex_iterator
, чтобы получить каждый блок последовательно.
Анализ содержимого блока
Используйте std::regex_iterator
с регулярным (S )s*=s*(".*?");
выражением . Назначьте результаты на std::map
.
Ответ №2:
Вы можете использовать регулярное выражение для сопоставления строк и извлечения из них данных. регулярное выражение является частью стандартной библиотеки так же, как std::строка и std::вектор. Это пример:
#include <iostream>
#include <regex>
#include <string>
#include <sstream>
struct ModelData
{
std::string tagName;
std::string filePath;
std::string texturePath;
std::string normalPath;
};
// helper function to output the content of your structure.
std::ostreamamp; operator<<(std::ostreamamp; os, const ModelDataamp; data)
{
os << "Model = " << data.tagName << "n";
os << " filePath = " << data.filePath << "n";
os << " texturePath = " << data.texturePath << "n";
os << " normalPath = " << data.normalPath << "n";
os << std::endl;
return os;
}
// load from a stream, so this example can use
// a stringstream instead of a filestream (doesn't change this function)
auto load_from_stram(std::istreamamp; ifile)
{
// More on regex here : https://regexone.com/, or to test your own regular expressions go here https://regex101.com/
// between ( ) is a capture group and will contain the value of your variable
// . wil match one or more of any character
static std::regex model_rx{ "MODEL = (. )" };
static std::regex filepath_rx{ "FILEPATH = (. )" };
static std::regex texturepath_rx{ "TEXTUREPATH = (. )" };
static std::regex normalpath_rx{ "NORMALPATH = (. )" };
std::smatch match;
std::vector<ModelData> models;
std::string line;
ModelData data;
// read until end of file
while (std::getline(ifile,line))
{
// check if a model starts,
// condition next 3 lines MUST contain data too
if (std::regex_search(line, match, model_rx))
{
// match[0] will contain full regex match
// match[1] will contain first matched group
data.tagName = match[1];
for (std::size_t n = 0; n < 3; n)
{
std::getline(ifile, line);
if (std::regex_search(line, match, filepath_rx)) data.filePath = match[1];
if (std::regex_search(line, match, texturepath_rx)) data.texturePath = match[1];
if (std::regex_search(line, match, normalpath_rx)) data.normalPath = match[1];
}
models.push_back(data);
}
}
return models;
}
int main()
{
std::istringstream ifile{ "MODEL = modelname1nFILEPATH = FILEPATH1nTEXTUREPATH = TEXTUREPATH1nNORMALPATH = NORMALPATH1n
MODEL = modelname2nFILEPATH = FILEPATH2nTEXTUREPATH = TEXTUREPATH2nNORMALPATH = NORMALPATH2n" };
auto models = load_from_stram(ifile);
for (const autoamp; model : models)
{
std::cout << model;
}
}
Комментарии:
1. хм, выглядит хорошо, но предполагается, что файл конфигурации проще и содержит содержимое в каждой строке, мне больше хочется знать, что для моего нового форматирования… что делать, если содержимое находится там с парой фигурных скобок. какая структура данных нужна для их анализа.
2. Приведенный выше анализ позволяет использовать emtpy или другие строки между моделями, он просто предполагает, что после каждой модельной строки следуют 3 строки. В конце концов, это минималистичный пример, но я думаю, что он может помочь вам начать 🙂
Ответ №3:
Я только что наткнулся на отличный учебник по разбору конфигурационного файла, и этот парень использовал токенизатор и проходил символ за символом, используя токены.
Я хочу знать, какова стратегия создания токена и токенизатора.