高级文件和字符串操作

Advanced File and String Operations

所以我正在我的 DirectX 11 程序中处理模型加载器,我 运行 解决了我认为是一个独特的问题。所以我花了一些时间寻找解决方案,但没有成功。我的问题是,在我的具有纹理路径和顶点列表的文件中,我希望能够挑选出某些部分,并删除一些部分。在我的无纹理三角形示例文件下方:

T:0$
(0, 5, 0)
(5, 0, 0)
(-5, 0, 0)

^ 这是旧的,请查看下面的编辑内容 ^

让我解释一下这里发生了什么。首先,"T:___" 是我的纹理文件路径。我已将其设置为“0”,因为我没有使用纹理。 "T:0" 之后的“$”是我的程序对文件路径结束和顶点开始的标记。

现在,这是我需要我的程序做的事情。

1. 读取文件直到到达字符“$”。然后删除前两个字符("T:")和“$”(如果已添加)。最后将剩余的文本放入一个名为 TextureData 的字符串中。 P.S。不要从文件中删除 "T:",只删除我的字符串(文件需要保持不变)。

2. 将剩余的文本(顶点)放入名为 VertexData 的临时字符串中,然后可能会删除括号..?我想知道如何做到这一点,但目前可能不会使用它。

我希望我自己和我的问题足够清楚。

提前致谢。

--- 重要编辑 ---

我稍微更改了格式,我查看了一个 .obj 文件并决定这样做会更容易。我的纹理和顶点文件现在看起来像这样:

T:0$
v 0 5 0
v 5 0 0
v -5 0 0

--- 编辑结束 ---

这里的代码是我的基础:

模型加载函数:

bool LoadTVF(string FP)
{
    ifstream TVFReader;
    TVFReader.open(FP);
    if (TVFReader.is_open())
    {
        ReadLine(1);    // Function not fully working, need to improve
        // Load vertices and texture into strings
        TVFReader.close();
        return true;
    }
    else
    {
        return false;
    }
}

ReadLine函数(只是为了组织我的代码并跳转到某一行并检索该行数据并放入一个字符串,字符串的切割和修改需要回到主函数):

string ReadLine(int character)
{
    return lineData;    // I know this doesn't work, just don't know what to return?
}

老实说,这个 ReadLine 函数,我不知道我在做什么。我只是制作某种框架来向您展示我希望如何组织代码。

再次感谢。

到目前为止,您似乎可以使用 getline 逐行读取文件,然后提取必要的信息:通过检查第一个字符找出 "line type",然后根据类型获取子字符串路径或将线分割成块并将它们转换为浮点数。

稍后 when/if 您的文件格式变得更加复杂,您可能想使用某种解析器生成器编写一个完整的解析器。

我会这样做:

bool LoadTVF(string FP)
{
    ifstream TVFReader;
    TVFReader.open(FP);
    if (TVFReader.is_open()) {
        stringstream buffer;
        buffer << TVFReader.rdbuf();
        string texture_string = buffer.str();
        texture_string = texture_string.substr(2, texture_string.length());
        // Removes T:
        texture_string.erase(texture_string.begin() + texture_string.find("$"));
        // Removes the first occurrence of $
        std::cout << texture_string;
        /* This will print out:
         * 0
         * v 0 5 0
         * v 5 0 0
         * v -5 0 0
         */
        TVFReader.close();
        return true;
    }
    else
    {
        return false;
    }
}

这不使用 ReadLine 函数,因为它一次加载整个字符串。由于我只是在开始时处理该字符串,所以我宁愿将解析和修剪逻辑发生在一个地方。

如果您必须逐行迭代,已经有一种方法可以使用 getline:

bool LoadTVF(string FP)
{
    ifstream TVFReader;
    TVFReader.open(FP);
    if (TVFReader.is_open()) {
        string line;
        while(getline(TVFReader, line))
        {
            // Do stuff with each line
        }
        TVFReader.close();
        return true;
    }
    else
    {
        return false;
    }
}

请注意,这些 line 中没有行尾字符。

编辑: 根据 OP 的评论,以下是将已解析文件拆分为单独字符串的方法:

bool LoadTVF(string FP)
{
    ifstream TVFReader;
    TVFReader.open(FP);
    if (TVFReader.is_open()) {
        string header;
        getline(TVFReader, header);
        header = header.substr(2, header.length());
        // Removes T:
        header.erase(header.begin() + header.find("$"));
        // Removes the first occurrence of $
        std::cout << header << std::endl;
        // This should print 0, which is the string between T: and $

        stringstream buffer;
        string line;
        while (getline(TVFReader, line))
        {
            line = line.substr(2, line.length());
            // Removes the starting "v "
            buffer << line << std::endl;
            // We need the end-of-line here because its been stripped
        }
        string texture_string = buffer.str();
        std::cout << texture_string;
        /* This will print out:
         * 0 5 0
         * 5 0 0
         * -5 0 0
         */
        TVFReader.close();
        return true;
    }
    else
    {
        return false;
    }
}

我想在这里做几点说明。如果您可以控制文件的结构方式,则不需要 $ 字符来指示该行的结尾。有一个换行符的事实应该足以说明这一点。此外,由于之后的每一行都代表矩阵中的一个向量,所以我认为 v 也不是必需的。最后,如果您的文件命名适当,T: 也可能是不必要的,例如 "test.texture"。这样你就知道你正在阅读的文件是一个纹理。

这会降低解析这些文件的复杂性,作为奖励,还会减少这些文件的存储大小。