读取UTF-8文件需要解析字符

Reading a UTF-8 file a parsing characters

本文关键字:字符 UTF-8 文件 读取      更新时间:2023-10-16

我最初使用std::wstring编写下面的代码,并使用静态键入到代码中的宽字符串。

后来我了解到UTF-8将"适合"std::string,并没有真正需要std::wstring,但我以后可能需要一些编码翻译。所以我有一个UTF-8编码的文本文件,我正在阅读。

#include <iostream>
#include <fstream>
class A
{
public:
A(std::istream& stream)
:
m_stream(stream),
m_lineNumber(1),
m_characterNumber(1)
{
}
bool OutputKnownWords()
{
while(m_stream.good())
{
if(Take("MIDDLE"))
std::cout << "Found middle" << std::endl;
else if(Take("BEGIN"))
std::cout << "Found begin" << std::endl;
else if(Take("END"))
std::cout << "Found end" << std::endl;
else if(Take(" "))
std::cout << "parsed out space" << std::endl;
else
return false;
}
return true;
}
protected:
std::istream::char_type Get()
{
auto c = m_stream.get();
++m_characterNumber;
if(c == 'n')
{
++m_lineNumber;
m_characterNumber = 1;
}
return c;
}
bool Take(const std::string& str)
{
if(!Match(str))
return false;
for(std::string::size_type i = 0; i < str.size(); ++i)
Get();
return true;
}
bool Match(const std::string& str)
{
auto cursorPos = m_stream.tellg();
std::string readStr(str.size(),'');
m_stream.read(&readStr[0],str.size());
if(std::size_t(m_stream.gcount()) < str.size() || readStr != str)
{
if(!m_stream.good())
m_stream.clear();
m_stream.seekg(cursorPos);
return false;
}
m_stream.seekg(cursorPos);
return true;
}
std::istream& m_stream;
std::size_t m_lineNumber;
std::size_t m_characterNumber;
};
int main()
{
std::ifstream file("test.txt");
if(!file.is_open())
{
std::cerr << "could not open file" << std::endl;
return 0;
}
A a(file);
if(!a.OutputKnownWords())
{
std::cerr << "something went wrong" << std::endl;
return 0;
}
return 0;
}

text.text

BEGIN MIDDLE
END

所以我希望这个程序输出:

Found begin
parsed out space
Found middle
parsed out space
Found end

但是,OutputKnownWords返回一个错误。我逐步调试了调试器,发现Match中的seekg调用似乎没有设置正确的位置。就像每个测试都是由一个角色完成的。

当我使用静态键入的宽字符串时,我没有问题。

我认为这可能与UTF-8编码与std::string的"字符"概念之间的差异有关。但我不知道如何处理std::string中有多少"字符"。

这和tellg((函数是否给出了错误的文件大小有关?因为除了使用tellg中的光标重置位置之外,我没有对其执行任何操作。

代码的一个更简单、更高效的版本是:

#include <iostream>
#include <fstream>
#include <string>
class A
{
public:
A(std::istream& stream)
:
m_stream(stream),
m_lineNumber(0),
m_characterNumber(0)
{
}
bool OutputKnownWords()
{
while (m_stream.good())
{
if (Take("MIDDLE"))
std::cout << "Found middle" << std::endl;
else if (Take("BEGIN"))
std::cout << "Found begin" << std::endl;
else if (Take("END"))
std::cout << "Found end" << std::endl;
else if (Take(" "))
std::cout << "parsed out space" << std::endl;
else
return !m_stream.good();
}
return true;
}
protected:
bool Take(const std::string& str)
{
if (!Match(str))
return false;
m_characterNumber += str.size();
return true;
}
bool readLine()
{
std::getline(m_stream, line);
m_characterNumber = 0;
m_lineNumber++;
return !m_stream.eof();
}
bool Match(const std::string& str)
{
while (m_characterNumber >= line.size())
{
if (!readLine())
{
return false;
}
}
if (line.size() - m_characterNumber < str.size())
{
return false;
}
return line.substr(m_characterNumber, str.size()) == str;
}
std::istream& m_stream;
std::size_t m_lineNumber;
std::size_t m_characterNumber;
std::string line;
};
int main()
{
std::ifstream file("test.txt");
if (!file.is_open())
{
std::cerr << "could not open file" << std::endl;
return 0;
}
A a(file);
if (!a.OutputKnownWords())
{
std::cerr << "something went wrong" << std::endl;
return 0;
}
return 0;
}

我不确定您最终想要实现什么,但您的代码似乎过于复杂,难以理解和调试。有几个问题。首先,您永远不会使用换行符,因为您只会为字符串中的字符数调用Get(如果你调试,你会注意到你在Match中读取的字符串是"nEN",而不是"END"。第二,如果你试图读取一个字符串,但流没有返回所需的字符数,你会清除错误标志并返回,这意味着你永远不会到达文件的末尾,stream.good()的while条件永远不会失败。

要解决这些问题,请将Match更改为:

bool Match(const std::string& str)
{
auto cursorPos = m_stream.tellg();
if (m_stream.peek() == 'n')
{
//consume the newline
Get();
cursorPos = m_stream.tellg();
}
std::string readStr(str.size(), '');
m_stream.read(&readStr[0], str.size());
if (m_stream.gcount() == 0)
{
// must be at EOF
return false;
}
if (std::size_t(m_stream.gcount()) < str.size() || readStr != str)
{
std::cout << "expected '" << str << "' actual '" << readStr << "'n";
if (!m_stream.good())
m_stream.clear();
m_stream.seekg(cursorPos);
return false;
}
m_stream.seekg(cursorPos);
return true;
}

OutputKnownWords更改为:

bool OutputKnownWords()
{
while (m_stream.good())
{
if (Take("MIDDLE"))
std::cout << "Found middle" << std::endl;
else if (Take("BEGIN"))
std::cout << "Found begin" << std::endl;
else if (Take("END"))
std::cout << "Found end" << std::endl;
else if (Take(" "))
std::cout << "parsed out space" << std::endl;
else
// stream can only be not good at EOF
return !m_stream.good();
}
return true;
}