阅读您的源代码后,我假设您正在学习 C++ iostream 功能。正则表达式和其他东西在开始时可能仍然太复杂。因此,让我们尝试使用正常的 iostream 功能来生存。
基本上你应该知道有两种主要的输入类型:
要获得概览,请阅读 CPP 参考中关于 here 的内容。
您已经了解格式化的 IO,因为您已经在使用像 std::cout << "Hello" << '\n'; 或 std::cin >> word >> count 这样的语句。格式化输入函数从流中读取字符,解释它们(格式化)并将它们转换为目标类型。例如,如果你写int number; std::cin >> number;,然后按1、2、3,软件将读取字符'1'、'2'和'3',然后将其转换为整数。
在幕后还有一点点,但这个解释现在很好。请注意,IO 操作符,如提取操作符>> 总是返回调用它们的流。这允许链接此类语句。 std::cin >> word >> count 的示例。首先执行std::cin >> word。将读取空格之前的所有字符,然后将其转换为字符串并存储在word 中。 >>operation 将返回调用它的流,在我们的例子中:std::cin。所以,在读完这个词之后,我们现在有了声明std::cin >> count。然后将读取计数。分隔空白将被忽略。这就是它基本工作的原因和方式。
另一个重要的属性是>> 操作会跳过任何潜在的空白(包括换行符'\n')然后开始转换。如果读取的字符不能再转换为目标变量,它将停止从输入流中读取和使用字符。这又包括空白。
如果流中有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。如您所见,它包含空格。而且由于格式化输入函数在空白处停止转换,我们不能使用像>>这样的格式化输入函数将完整的文本读入一个字符串变量。
现在我们需要使用像std::getline 这样的无格式输入函数。这将读取所有字符,直到给定分隔符,并将其存储在 std::string 变量中。分隔符通常是 '\n',因此std::getline 将读取一个完整的行,直到换行符 '\n'。但我们也可以指定不同的分隔符,例如 ')' 或其他。
请记住,格式化输入函数会返回对给定字符串的引用。所以,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 也返回流。知道了以上,我们就可以得出一条线的最终解决方案了。
#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 操作返回对流的引用。我们可以这样写:
if (std::getline(test >> line >> c1 >> c2 >> time >> c3 >> c4 >> type >> c5 >> c6, comment, ')') >> c7) {
...
}
但是怎么做呢?正如所说。输入函数将返回对流的引用,然后我们离开了:if (test)。这是可行的,因为流的 bool 运算符被覆盖(请参阅here)。这个布尔运算符会告诉我们是否有错误。
所以,要读取和输出所有数据,我们可以这样写:
#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++ 是一种所谓的面向对象语言。在这里,我们将处理这些数据的方法中的数据放入class 或struct 中。
这可能看起来像:
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,更好的是,将它封装在一个额外的类中。
例如:
#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 中的一行用于读取和解析整个文件。
如果您有任何问题,我很乐意回答。