解析带符号的文本文件

Parsing text file with symbol

我无法解析 .txt 文件中的所有文本 但是当我 运行 我的代码时,我没有得到我想要的:(

我的代码:

int main()
{
  /* inside "logMsg.txt"
  1/ [111]{1}(text line from 111);
  2/ [222]{2}(text line from 222);
  3/ [333]{3}(text line from 333);
  */

  ifstream textfile("logMsg.txt");

  string log_line;
  string log_time;
  string log_type;
  string log_comment;

  // Get line number
  getline(textfile, log_line, '/');

  // Get Time by unix
  getline(textfile, log_time, '[');
  getline(textfile, log_time, ']');

  // Get type of the function
  getline(textfile, log_type, '{');
  getline(textfile, log_type, '}');

  // Get comment from data_
  getline(textfile, log_comment, '(');
  getline(textfile, log_comment, ')');

  cout << "Line Of the log: " << log_line << "\n";
  cout << "Type of the log: " << log_type << "\n";
  cout << "Time of the log: " << log_time << "\n";
  cout << "Comment of the log :" << log_comment << "\n";
}

控制台:

Line Of the log: 1
Type of the log: 1
Time of the log: 111
Comment of the log :text line from 111

我希望解析器读取所有行,但结果完全不同

我不明白我做错了什么,我试图得到以下结果:

Line Of the log: 1, 2, 3,
Type of the log: 1, 2, 3,
Time of the log: 111, 222, 333,
Comment of the log: text line from 111, text line from 222, text line from 333,

请帮忙:(

我建议为此使用正则表达式:

  • 下面的示例只是使用给定的模式解析每一行。您可能需要对其进行修改,例如,允许在某些字段周围使用空格。

[Demo]

#include <fmt/core.h>
#include <iostream>  // cout
#include <regex>
#include <sstream>  // istringstream
#include <string>  // getline

int main() {
    const std::string input{"1/ [111]{1}(text line from 111);\n2/ [222]{2}(text line from 222);\n3/ [333]{3}(text line from 333);"};
    std::istringstream iss{input};

    std::string line{};
    while (std::getline(iss, line))
    {
        const std::regex pattern{R"(([\d+])/\s*\[(\d+)\]\{(\d+)\}\(([^)]+)\);)"};
        std::smatch matches{};
        if (std::regex_match(line, matches, pattern))
        {
            std::cout << fmt::format ("Line: {}, type: {}, time: {}, comment: {}\n",
                matches[1].str(), matches[2].str(), matches[3].str(), matches[4].str());
        }
    }
}

// Outputs:
//
//   Line: 1, type: 111, time: 1, comment: text line from 111
//   Line: 2, type: 222, time: 2, comment: text line from 222
//   Line: 3, type: 333, time: 3, comment: text line from 333
  • 如果您想要不同的输出(例如,您在问题中似乎建议打印所有行号,然后一直打印,依此类推),您可能需要使用 std::vectors 来保持该信息,并在最后打印出来。

[Demo]

#include <algorithm>  // copy
#include <iostream>  // cout
#include <iterator>  // ostream_iterator
#include <regex>
#include <sstream>  // istringstream
#include <string>  // getline

int main() {
    const std::string input{"1/ [111]{1}(text line from 111);\n2/ [222]{2}(text line from 222);\n3/ [333]{3}(text line from 333);"};
    std::istringstream iss{input};

    std::vector<std::string> numbers{};
    std::vector<std::string> types{};
    std::vector<std::string> times{};
    std::vector<std::string> comments{};
    std::string line{};
    while (std::getline(iss, line))
    {
        const std::regex pattern{R"(([\d+])/\s*\[(\d+)\]\{(\d+)\}\(([^)]+)\);)"};
        std::smatch matches{};
        if (std::regex_match(line, matches, pattern))
        {
            numbers.push_back(matches[1]);
            types.push_back(matches[2]);
            times.push_back(matches[3]);
            comments.push_back(matches[4]);
        }
    }
    std::cout << "Line of the log: ";
    std::copy(numbers.cbegin(), numbers.cend(), std::ostream_iterator<std::string>(std::cout, ", "));
    std::cout << "\n";
    std::cout << "Type of the log: ";
    std::copy(types.cbegin(), types.cend(), std::ostream_iterator<std::string>(std::cout, ", "));
    std::cout << "\n";
    std::cout << "Time of the log: ";
    std::copy(times.cbegin(), times.cend(), std::ostream_iterator<std::string>(std::cout, ", "));
    std::cout << "\n";
    std::cout << "Comment of the log: ";
    std::copy(comments.cbegin(), comments.cend(), std::ostream_iterator<std::string>(std::cout, ", "));
    std::cout << "\n";
}

// Outputs:
//
//   Line of the log: 1, 2, 3, 
//   Type of the log: 111, 222, 333, 
//   Time of the log: 1, 2, 3, 
//   Comment of the log: text line from 111, text line from 222, text line from 333, 
  • 尽管您最好使用 struct 中的单个 std::vector,并为数字、类型和时间设置适当的类型。在这种情况下,您需要将字符串从 matches 转换为 structint(您可以使用 std::stoi)。

[Demo]

struct LineInfo
{
    int number{};
    int type{};
    int time{};
    std::string comment{};
};
std::vector<LineInfo> line_infos{};

阅读您的源代码,我假设您正在学习 C++ iostream 功能。正则表达式之类的东西一开始可能还是太复杂了。因此,让我们尝试使用正常的 iostream 功能生存。

基本上你应该知道有两种主要的输入类型:

  • 格式化输入
  • 未格式化的输入

要了解概览,请阅读有关 here 的 CPP 参考资料。

您已经了解格式化 IO,因为您一直在使用 std::cout << "Hello" << '\n';std::cin >> word >> count 等语句。格式化输入函数从流中读取字符,解释它们(格式化)并将它们转换为目标类型。例如,如果您输入 int number; std::cin >> number;,然后按 1、2、3 键,软件将读取字符“1”、“2”和“3”,然后将其转换为整数。

在幕后还有更多内容,但现在这个解释还不错。请注意 IO-operators,就像提取运算符 >> 一样,总是 return 它们被调用的流。这允许链接此类语句。 std::cin >> word >> count 的示例。首先 std::cin >> word 被执行。直到白色 space 之前的所有字符将被读取,然后转换为字符串并存储在 word 中。 >> 操作将 return 调用它的流,在我们的例子中:std::cin。所以,读完这个词后,我们现在剩下语句 std::cin >> count 了。然后将读取计数。分隔白色 space 将被忽略。这就是它基本起作用的原因和方式。

一个更重要的属性是>>操作跳过任何潜在的白色space(包括换行符'\n')然后开始转换。如果读取的字符不能再转换为目标变量,它将停止读取和使用输入流中的字符。这又包括白色 space.

如果流中有 1/ [111]{1}(text line from 111); 并写入 stream >> number;,那么它将读取字符 1 并开始转换为整数。然后它会看到以下“/”字符。它停止读取并将“1”转换为数字 1,但是,它不会从流中提取“/”。它仍然存在,可以而且必须阅读。因此,如果我们定义一个 char 变量 c1 并写入 stream >> number >> c1 那么首先数字将被读取为 number 并且 '/' 将被读取为 c1.空格将被忽略。

通过使用这种机制,您可以像这样读取字符串的第一部分:

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <iomanip>

std::istringstream test{ R"(1/[111]{1}(text line from 111);)" };

int main() {
    int line, time, type;
    char c1,c2,c3,c4,c5,c6;
    std::string comment;

    test >> line >> c1 >> c2 >> time >> c3 >> c4 >> type >> c5 >> c6;

    return 0;
}

(cx 变量只是虚拟变量)

变量将是:

这符合预期。空间不会以任何方式打扰这里。

现在是下一部分,注释,所以字符串中的 text line from 111。如您所见,它包含 spaces。并且由于格式化输入函数在白色 space 处停止转换,我们不能使用像 >> 这样的格式化输入函数将完整的文本读入一个字符串变量。

现在我们需要使用像std::getline这样的无格式输入函数。这将读取所有字符,直到给定的分隔符并将其存储在 std::string 变量中。分隔符通常是'\n',这样std::getline会读完整行直到换行符'\n'。但我们也可以指定不同的分隔符,例如 ')' 或其他任何内容。

请记住格式化输入功能 return 对给定字符串的引用。因此,test >> line >> c1 >> c2 >> time >> c3 >> c4 >> type >> c5 >> c6 将导致流 test。由于 std::getline 需要一个流作为第一个参数,我们可以将完整的长输入语句传递到 std::getline 并写入:

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <iomanip>

std::istringstream test{ R"(1/[111]{1}(text line from 111);)" };

int main() {
    int line, time, type;
    char c1,c2,c3,c4,c5,c6;
    std::string comment;

    std::getline(test >> line >> c1 >> c2 >> time >> c3 >> c4 >> type >> c5 >> c6, comment, ')');

    return 0;
}

结果将是

你猜怎么着? std::getline 也 return 流。知道了这一点和上面的内容,我们可以得出一行的最终解决方案。

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <iomanip>

std::istringstream test{ R"(1/[111]{1}(text line from 111);)" };

int main() {
    int line, time, type;
    char c1,c2,c3,c4,c5,c6, c7;
    std::string comment;

    std::getline(test >> line >> c1 >> c2 >> time >> c3 >> c4 >> type >> c5 >> c6, comment, ')') >> c7;

    return 0;
}

下一步:检查IO操作是否成功。

我们应该始终检查任何 IO 操作的结果。但是怎么办?我已经解释过 IO-operations return 是对流的引用。这样我们就可以写成:

if (std::getline(test >> line >> c1 >> c2 >> time >> c3 >> c4 >> type >> c5 >> c6, comment, ')') >> c7) {
...
}

但是怎么办?正如所说。输入函数将 return 引用流,然后我们离开:if (test)。这行得通,因为流的 bool 运算符被覆盖了(参见 here)。这个 bool 运算符会告诉我们是否有错误。

所以,要读取和输出所有数据,我们可以这样写:

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <iomanip>

std::istringstream test{ R"(1/[111]{1}(text line from 111);
  2/ [222]{2}(text line from 222);
  3/ [333]{3}(text line from 333);)" };

int main() {
    int line, time, type;
    char c1,c2,c3,c4,c5,c6, c7;
    std::string comment;
    while (std::getline(test >> line >> c1 >> c2 >> time >> c3 >> c4 >> type >> c5 >> c6, comment, ')') >> c7)
       std::cout << "Line: " << line << "\tTime: " << time << "\tType: " << type << "\tComment: " << comment << '\n';
}

这表明它基本上是如何工作的。


但是,您需要不同的输出格式。这不是水平的,而是垂直的。因此,我们需要将读取的内容存储在某个动态容器中。我希望你知道 std::vector。如果没有,请告诉我,我也会在这里帮助你。

所以我们不会读取变量,输出它们然后忘记它,而是存储它们以备后用。

您可能听说过 C++ 是一种所谓的面向对象的语言。在这里,我们将处理这些数据的方法中的数据放入一个 classstruct.

这可能看起来像:

struct LogLine {
    int line{};
    long time{};
    int type{};
    std::string comment{};
};

这是数据部分。现在,我们需要这些数据的方法。我们唯一需要的就是输入。为此,我们可以将提取操作 >> 添加到此结构中。语法是:

std::istream& operator >> (std::istream& is, LineInfo& l) { ....

结合我们上面学到的东西我们现在可以写:

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <iomanip>

std::istringstream test{ R"(1/[111]{1}(text line from 111);
  2/ [222]{2}(text line from 222);
  3/ [333]{3}(text line from 333);)" };

struct LogLine {
    // Data
    int line{};
    long time{};
    int type{};
    std::string comment{};

    // Methods
    friend std::istream& operator >> (std::istream& is, LogLine& l) {
        char c1, c2, c3, c4, c5, c6, c7;
        return std::getline(is >> l.line >> c1 >> c2 >> l.time >> c3 >> c4 >> l.type >> c5 >> c6 >> std::ws, l.comment, ')') >> c7;
    }
    friend std::ostream& operator << (std::ostream& os, const LogLine& l) {
        return os << "Line: " << l.line << "\tTime: " << l.time << "\tType: " << l.type << "\tComment: " << l.comment;
    }
};

这要好得多。

现在我们可以为这些数据创建一个 std::vector 甚至更好,将其封装在一个额外的 class.

例如:

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <vector>

std::istringstream test{ R"(1/[111]{1}(text line from 111);
  2/ [222]{2}(text line from 222);
  3/ [333]{3}(text line from 333);)" };

struct LogLine {
    // Data
    int line{};
    long time{};
    int type{};
    std::string comment{};

    // Methods
    friend std::istream& operator >> (std::istream& is, LogLine& l) {
        char c1, c2, c3, c4, c5, c6, c7;
        return std::getline(is >> l.line >> c1 >> c2 >> l.time >> c3 >> c4 >> l.type >> c5 >> c6 >> std::ws, l.comment, ')') >> c7;
    }
    friend std::ostream& operator << (std::ostream& os, const LogLine& l) {
        char c1, c2, c3, c4, c5, c6, c7;
        return os << "Line: " << l.line << "\tTime: " << l.time << "\tType: " << l.type << "\tComment: " << l.comment;
    }
};



struct Log {
    // Data
    std::vector<LogLine> logLines;
    // Methods
    friend std::istream& operator >> (std::istream& is, Log& l) {
        l.logLines.clear(); LogLine tmp{};
        while (is >> tmp) l.logLines.push_back(tmp);
        return is;
    }
    friend std::ostream& operator << (std::ostream& os, const Log& l) {
        os << "\nLine Of the log : "; for (const LogLine& ll : l.logLines) os << ll.line << ", ";
        os << "\nType of the log : "; for (const LogLine& ll : l.logLines) os << ll.type << ", ";
        os << "\nTime of the log : "; for (const LogLine& ll : l.logLines) os << ll.time << ", ";
        os << "\nComment of the log : "; for (const LogLine& ll : l.logLines) os << ll.comment << ", ";
        return os;
    }
};

int main() {
    Log log;
    
    // Read and parse all data
    test >> log;

    // Show debug output
    std::cout << log;
}

这为您提供了惯用的正确解决方案。

如您所见,我们将一个大问题拆分为许多小问题。先解决输入部分,然后再封装成结构体。

结果是 main 中的一行用于读取和解析整个文件。

如果您有任何问题,我很乐意回答。