代码之家  ›  专栏  ›  技术社区  ›  NeomerArcana

读取UTF-8文件a解析字符

c++
  •  1
  • NeomerArcana  · 技术社区  · 8 年前

    我最初使用以下代码 std::wstring 使用静态输入到代码中的宽字符串。

    后来我知道UTF-8将“适合”于 std::string 也没有必要 标准::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(),'\0');
    
            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

    BEGIN MIDDLE
    END
    

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

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

    然而, OutputKnownWords 返回错误。我通过调试器发现 seekg 来电 Match 似乎没有设置正确的位置。就像,每个测试都是由一个字符完成的。

    当我用静态输入的宽字符串做这个时,我没有问题。

    我认为这可能与UTF-8编码与 标准::字符串 “性格”的概念。但我不知道该如何处理 标准::字符串 .

    这与 tellg() function give wrong size of file? 因为我不会用光标 tellg 而不是用它来重置位置。

    2 回复  |  直到 8 年前
        1
  •  1
  •   Alan Birtles    8 年前

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

    #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;
    }
    
        2
  •  0
  •   Alan Birtles    8 年前

    我不知道你到底想实现什么,但你的代码似乎不必要的复杂,难以理解和调试。有几个问题。第一个是你从不使用换行符,因为你只调用 Get 对于字符串中的字符数(如果进行调试,您将注意到读取的字符串 Match 是 "\nEN" 而不是 "END" . 第二种情况是,如果试图读取字符串,但流未返回所请求的字符数,则清除错误标志并返回,这意味着您永远无法到达文件的结尾,而while条件为 stream.good() 永远不会失败。

    要解决这些问题,请更改 匹配 致:

    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(), '\0');
    
        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;
    }