读取UTF-8文件需要解析字符
Reading a UTF-8 file a parsing characters
我最初使用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;
}
相关文章:
- HEX值到wchar_t字符(UTF-8)的转换
- 转换特殊字符(UTF-8)
- 在C++中使用 UTF-8 字符串和字符
- 在基于英语的系统上将 UTF-8 路径转换为宽字符会引发异常
- 无法将字符数组转换为包含 utf-8 字符的字符串
- 如何在 Visual C++ 中使用 UTF-8 字符串作为字符*?
- 读取UTF-8文件需要解析字符
- 如何正确处理渲染大小为 >= 2B 的 utf-8 字符?
- C++ UTF-8 瑞典语字符读取为 ASCII
- 如何C++ WCOUT UTF-16 编码的字符数组?
- 检查 UTF-8 是wchar_t还是字符?
- Qt UTF-8 文件到 std::string 添加额外的字符
- 字符的 UTF-8 转换
- C Unicode UTF-8解码字符的问题
- 将转义的 UTF-8 八位字节的字符数组转换为 C++ 的字符串
- 如何在没有wchar_t的情况下在 c++ 中解码/编码 UTF-8 字符
- 如何将 utf 字符转换为 windows-1252?
- 在 Linux 中将 UTF-32 宽字符转换为 UTF-16 宽字符以获取补充平面字符
- C 14:UTF-8/UTF-16与本机字符编码之间的转换
- 从键盘读取 UTF-8 编码的字符