为什么我的文件读取功能将空终止符放在多余的 CR LF 车厢应该在的地方?
Why on earth is my file reading function placing null-terminators where excess CR LF carriages should be?
今天我尝试组装一个简单的 OpenGL 着色器 class,它从文件中加载文本,根据一些(非常棒的)进行一些解析以构建一对顶点和片段着色器) 自定义语法(例如,编写“.varying [type] [name];”将允许您在两个着色器中定义一个可变变量,而只需编写一次,与“.version”相同),然后编译一个 OpenGL 着色器程序使用两者,然后将着色器 class 标记为 'ready' 当且仅当着色器代码编译正确时。
现在,我做了所有这些,但后来遇到了最奇怪的(f运行kly 有点可怕)的问题。我设置了所有内容,用一些包含有效着色器代码的文件声明了一个新的 'tt::Shader',只是让它告诉我着色器无效但是当我询问错误是什么时给我一个空字符串(这意味着 OpenGL给了我一个空字符串,因为它是从那里得到的。)
我再次尝试,这次使用明显无效的着色器代码,虽然它确定着色器无效,但它仍然没有告诉我错误是什么,只是一个空字符串(我从中假设显然错误识别部分也和之前一样。)
困惑,我重新编写了两个着色器,有效的和无效的,作为一个字符串手工编写,直接用字符串再次编译 classes,没有文件访问。这样做,错误消失了,第一个编译正确,第二个失败但正确识别错误是什么。
更困惑的是,我开始将文件中的字符串与我自己编写的字符串进行比较。原来前者比梯子长一点,尽管打印相同。数了一下,我发现这些字符一定是 Windows CR LF 行尾回车字符,在导入过程中被截断了。
为了测试这一点,我采用了手写字符串,在它们将被切断的地方插入了回车,然后 运行 我的字符串比较测试再次进行。这次测出来两个长度一样,还告诉我两个哪里不相等,很纳闷。
因此,我编写了一个简单的 for 循环来遍历两个字符串的字符,然后逐个打印,然后转换为整数,这样我就可以看到它们的索引值。我 运行 程序,查看了(相当长的)列表,得出了一个有见地但更不明确的答案:隐藏的字符在正确的位置,但它们不是马车......它们是空终止符!
这是我正在使用的文件读取功能的代码。没什么特别的,只是标准库的东西。
// Attempts to read the file with the given path, returning a string of its contents.
// If the file could not be found and read, an empty string will be returned.
// File strings are build by reading the file line-by-line and assembling a single with new lines placed between them.
// Given this line-by-line method, take note that it will copy no more than 4096 bytes from a single line before moving on.
inline std::string fileRead(const std::string& path) {
if (!tt::fileExists(path))
return "";
std::ifstream a;
a.open(path);
std::string r;
const tt::uint32 _LIMIT = 4096;
char r0[_LIMIT];
tt::uint32 i = 0;
while (a.good()) {
a.getline(r0, _LIMIT);
if (i > 0)
r += "\n";
i++;
r += std::string(r0, static_cast<tt::uint32>(a.gcount()));
}
// TODO: Ask Whosebug why on earth our file reading function is placing null characters where excess carriages should go.
for (tt::uint32 i = 0; i < r.length(); i++)
if (r[i] == '[=10=]')
r[i] = '\r';
a.close();
tt::printL("Reading file '" + path + "' ...");
return r;
}
如果你们都可以阅读并告诉我它到底是怎么回事,那就太棒了,因为我完全不知道它对我的弦做了什么导致了这个。
最后,我确实明白为什么空终止符没有出现在我面前但出现在 OpenGL 上,阶梯使用的是 C 字符串,而我只是用 std::string 对象做所有事情,其中根据长度存储东西,因为它们几乎只是花哨的 std::vector 个对象。
阅读 documentation 的 std::string
构造函数。无论输入如何,构造函数 std::string(const char*, size_t n)
都会创建大小为 n
的字符串。它可能包含空字符,甚至超过 1 个。请注意 std::string
的大小不包括空字符(因此 str[str.size()] == '[=14=]'
始终如此)。
很明显,代码只是从 getline
函数的输出缓冲区复制空字符。
为什么要这样做?转到 gcount()
函数 documentation - 它 returns 上次操作提取的字符数。即,它包含提取的字符 \n
,它在输出中被替换为 [=18=]
瞧。正好比构造函数要求的多一个数。
所以要修复它只需替换:
r += std::string(r0, static_cast<tt::uint32>(a.gcount()-1));
或者您可以简单地使用 getline()
和 std::string
作为输入而不是缓冲区 - 这样 none 就会发生。
今天我尝试组装一个简单的 OpenGL 着色器 class,它从文件中加载文本,根据一些(非常棒的)进行一些解析以构建一对顶点和片段着色器) 自定义语法(例如,编写“.varying [type] [name];”将允许您在两个着色器中定义一个可变变量,而只需编写一次,与“.version”相同),然后编译一个 OpenGL 着色器程序使用两者,然后将着色器 class 标记为 'ready' 当且仅当着色器代码编译正确时。
现在,我做了所有这些,但后来遇到了最奇怪的(f运行kly 有点可怕)的问题。我设置了所有内容,用一些包含有效着色器代码的文件声明了一个新的 'tt::Shader',只是让它告诉我着色器无效但是当我询问错误是什么时给我一个空字符串(这意味着 OpenGL给了我一个空字符串,因为它是从那里得到的。)
我再次尝试,这次使用明显无效的着色器代码,虽然它确定着色器无效,但它仍然没有告诉我错误是什么,只是一个空字符串(我从中假设显然错误识别部分也和之前一样。)
困惑,我重新编写了两个着色器,有效的和无效的,作为一个字符串手工编写,直接用字符串再次编译 classes,没有文件访问。这样做,错误消失了,第一个编译正确,第二个失败但正确识别错误是什么。
更困惑的是,我开始将文件中的字符串与我自己编写的字符串进行比较。原来前者比梯子长一点,尽管打印相同。数了一下,我发现这些字符一定是 Windows CR LF 行尾回车字符,在导入过程中被截断了。
为了测试这一点,我采用了手写字符串,在它们将被切断的地方插入了回车,然后 运行 我的字符串比较测试再次进行。这次测出来两个长度一样,还告诉我两个哪里不相等,很纳闷。
因此,我编写了一个简单的 for 循环来遍历两个字符串的字符,然后逐个打印,然后转换为整数,这样我就可以看到它们的索引值。我 运行 程序,查看了(相当长的)列表,得出了一个有见地但更不明确的答案:隐藏的字符在正确的位置,但它们不是马车......它们是空终止符!
这是我正在使用的文件读取功能的代码。没什么特别的,只是标准库的东西。
// Attempts to read the file with the given path, returning a string of its contents.
// If the file could not be found and read, an empty string will be returned.
// File strings are build by reading the file line-by-line and assembling a single with new lines placed between them.
// Given this line-by-line method, take note that it will copy no more than 4096 bytes from a single line before moving on.
inline std::string fileRead(const std::string& path) {
if (!tt::fileExists(path))
return "";
std::ifstream a;
a.open(path);
std::string r;
const tt::uint32 _LIMIT = 4096;
char r0[_LIMIT];
tt::uint32 i = 0;
while (a.good()) {
a.getline(r0, _LIMIT);
if (i > 0)
r += "\n";
i++;
r += std::string(r0, static_cast<tt::uint32>(a.gcount()));
}
// TODO: Ask Whosebug why on earth our file reading function is placing null characters where excess carriages should go.
for (tt::uint32 i = 0; i < r.length(); i++)
if (r[i] == '[=10=]')
r[i] = '\r';
a.close();
tt::printL("Reading file '" + path + "' ...");
return r;
}
如果你们都可以阅读并告诉我它到底是怎么回事,那就太棒了,因为我完全不知道它对我的弦做了什么导致了这个。
最后,我确实明白为什么空终止符没有出现在我面前但出现在 OpenGL 上,阶梯使用的是 C 字符串,而我只是用 std::string 对象做所有事情,其中根据长度存储东西,因为它们几乎只是花哨的 std::vector 个对象。
阅读 documentation 的 std::string
构造函数。无论输入如何,构造函数 std::string(const char*, size_t n)
都会创建大小为 n
的字符串。它可能包含空字符,甚至超过 1 个。请注意 std::string
的大小不包括空字符(因此 str[str.size()] == '[=14=]'
始终如此)。
很明显,代码只是从 getline
函数的输出缓冲区复制空字符。
为什么要这样做?转到 gcount()
函数 documentation - 它 returns 上次操作提取的字符数。即,它包含提取的字符 \n
,它在输出中被替换为 [=18=]
瞧。正好比构造函数要求的多一个数。
所以要修复它只需替换:
r += std::string(r0, static_cast<tt::uint32>(a.gcount()-1));
或者您可以简单地使用 getline()
和 std::string
作为输入而不是缓冲区 - 这样 none 就会发生。