如何避免阅读文本文件中的评论?
How to avoid reading comments in text file?
目前我已经使用这段代码成功地向我的文件写入了一些介于 0 到 10 之间的随机数(下面只是一些示例代码来演示这个问题):
for (int i = 1; i <= size; i++)
{
type = rand () % 3;
switch (type)
{
case 0: afile << rand () % 10;
break;
case 1: afile << rand () % 10;
afile << "\t\t";
afile << rand () % 10;
break;
case 2: afile << rand () % 10;
afile << "\t\t";
afile << rand () % 10;
afile << "\t\t";
afile << rand () % 10;
/*afile << "8";
afile << "\t";
afile << "7";
afile << "\t";
afile << "2";*/
}
afile << "\t\t" << "// Possible type " << i << endl;
}
然后我的 afile
在执行代码后看起来像这样:
8 // Possible type 1
1 7 // Possible type 2
4 0 3 // Possible type 3
当我从这个文件中读取数据并输出到另一个文件时出现问题:
int type;
while (afile >> type)
{
if(type == 0)
{
afile >> .........;
..........
}
else if(type == 1) {.........}
else if(type == 2) {.........}
}
}
......................................
我的输出文件在第一行后停止读取,因为它还读取了要处理的无效数据的注释,如果我删除注释,则一切正常。那么我该如何克服这种情况呢?谢谢。
有几种方法可以做到这一点。
找到引述后跳过一行的剩余部分(更快)
基本上你在这里要做的是在循环中逐行读取文件。当您点击两个字符“//”时。你会调用 "break;" 并跳到下一行。'
一些未经测试的虚拟代码:
while(line = file.getLine()){
loopChars = sizeof(line);
for(x = 0; x < loopChars; x++) {
char currentChar = line[x];
if(x+1 < loopChars){
char nextChar = line[x+1];
} else {
char nextChar = '';
}
if(nextChar == "/" && currentChar == "/"){
// Go to next line
break;
} else {
// Do your normal processing here
}
}
}
先删除引号(较慢)
这是一个从文件中删除引号(单行“//”和多行“/**/”)的解决方案。基本上,在开始读取数字数据之前,您会 运行 针对您正在处理的文件执行此操作。
http://www.cplusplus.com/forum/beginner/80380/
#include <iostream>
#include <fstream>
using namespace std;
int main (){
ifstream infile;
string filename;
ofstream outfile;
char c1, c2;
bool isInsideComment = false;
cout << "Please input file name (to remove comments from): ";
cin >> filename;
infile.open (filename.c_str());
if (infile.fail()) {
cout << "nInvaild file name.n";
return 1;
}
outfile.open(("out_"+filename).c_str());
infile.get (c1);
while (!infile.eof()) {
if ((c1 == '/') && (!isInsideComment)) {
infile.get (c2);
if (c2 == '*')
isInsideComment = true;
else if ((c1 == '/') && (c2 == '/'))
isInsideComment = true;
else {
outfile.put (c1);
outfile.put (c2);
}
}
else if ( (c1 == '*') && isInsideComment) {
infile.get (c2);
if (c2 == '/')
isInsideComment = false;
else if ((c1 == 'n') && isInsideComment)
isInsideComment = false;
}
else if (!isInsideComment)
outfile.put (c1);
infile.get (c1);
}
infile.close();
outfile.close();
}
您有几个合理的选择:
将整行读入 std::string
,扫描并删除任何评论,然后从剩下的任何内容创建一个 std::istringstream
并从中提取非评论值
在读取值之前,使用 >> std::ws
和 afile.peek()
查看下一个字符是否是 '/'
:如果是,请跳过,直到到达换行符。
前者是在 C++ 中习惯的有用技术(当您想报告带有数据问题的行号时会有所帮助),看起来像这样:
if (std::ifstream in(filename))
{
std::string line;
while (getline(in, line))
{
std::string::size_type n = line.find("//");
if (n != std::string::npos)
line.erase(n);
std::istringstream iss(line);
int atype;
while (iss >> atype)
...etc...
}
据我所知,Tony D 已经提供了一个合理的答案,但我想我也应该添加自己的代码,因为我已经编写并测试了它。
对于任何使用 C++ 的人来说,下面的内容几乎都是不言自明的,这基本上是 Tony 提出的,但有一点不同——逐行获取数据,利用 std::stringstream
、但随后也利用了 OP 使用的数据的二进制性质。那里的数据要么是一个有效的整数,要么是一条评论。或者换句话说,要么是有效整数,要么不是。所以在下面的代码中,当无法将数据从流有效转换为整数时 - 该行的其余部分被视为注释。 编辑:...实际上,虽然它是有点有效的解决方案,我修改了代码以纳入更明智的方法 - 一种跳过注释的方法(用 #
或 //
表示以显示两种方法)但仍然让我们决定要做什么在畸形值上。这不允许 45fubar
作为 45
传递,然后是错误的 fubar
,这是先前代码的问题,但允许 45//comment
被正确解释。
我仍然认为直接删除 \/\/.*?
是更好的方法。不过,这个答案的要点有点不同。 ;)
#include <ctime>
#include <cmath>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
void write(std::ostream& output, int lines) {
for (int i = 0; i < lines; i++) { // for i lines
int n = rand() % 10 + 1; // generate n numbers per line
for (int j = 0; j < n; j++) { // loop over line
output << rand() % 99; // output a random number
if (j + 1 < n) { // if not last?
output << "\t\t"; // then add tabs
}
}
output << " // " << n << " numbers\n"; // I'm not using std::endl here because it actually *flushes* the stream - flushing every iteration isn't advisable
}
}
std::vector<std::vector<int>> read(std::istream& input) {
std::vector<std::vector<int>> v; // a vector of vectors of ints
std::string line;
while (std::getline(input, line)) { // getline returns the stream by reference, so this handles EOF
std::stringstream ss(line); // create a stringstream out of line
int n = 0;
std::vector<int> numbers_in_line;
while (ss) { // while the stream is good
std::string word;
if (ss >> word) { // if there's still data to get
std::stringstream tester(word);
tester >> n;
if (tester && tester.peek() == std::char_traits<char>::eof()) { // conversion went well, no data was left in stream
numbers_in_line.push_back(n); // push it to the vector
} else { // conversion didn't go well, or went well but data was left in the stream
bool conversion_went_well = tester.good();
tester.clear();
char c = tester.get();
if (c == '#' || (c == '/' && tester.peek() == '/')) { // if it's a comment
if (conversion_went_well) {
numbers_in_line.push_back(n); // push it to the vector
}
break; // and ignore the rest of the line
} else {
std::cerr << "Unexpected symbol: '" << tester.str() << "'\n"; // report unexpected data
// so how do we handle a malformed value?
// error out? ignore following values in this line? accept following values in this line?
// if you leave it as is now - it will accept following values from this line
}
}
}
}
v.push_back(numbers_in_line);
}
return v;
}
int main(int argc, char** argv) {
std::srand(std::time(nullptr));
write(std::cout, 4); // write random data
std::vector<std::vector<int>> numbers = read(std::cin); // read the data
for (std::vector<int> line: numbers) { // loop over vector via C++11 features
for (int n: line) {
std::cerr << n << " ";
}
std::cerr << "\n";
}
return 0;
}
一个例子运行:
$ ./test.exe < data > data
50 44 92 43 97
26 32 54
30 91
93 4
$ cat data
50 44 92 43 97 // 5 numbers
26 32 54 // 3 numbers
30 91 // 2 numbers
93 4 // 2 numbers
$ ./test.exe < data2 > dump
Unexpected symbol: 'i91'
Unexpected symbol: '4i'
Unexpected symbol: 'lol'
Unexpected symbol: 'numbers'
50 44 92 43 97
26 32 54
30
93 3 2
7337
7337
$ cat data2
50 44 92 43 97 // 5 numbers
26 32 54 # 3 numbers
30 i91 // 2 numbers
93 4i lol 3 2 numbers
7337//test comment
7337#test comment 2
目前我已经使用这段代码成功地向我的文件写入了一些介于 0 到 10 之间的随机数(下面只是一些示例代码来演示这个问题):
for (int i = 1; i <= size; i++)
{
type = rand () % 3;
switch (type)
{
case 0: afile << rand () % 10;
break;
case 1: afile << rand () % 10;
afile << "\t\t";
afile << rand () % 10;
break;
case 2: afile << rand () % 10;
afile << "\t\t";
afile << rand () % 10;
afile << "\t\t";
afile << rand () % 10;
/*afile << "8";
afile << "\t";
afile << "7";
afile << "\t";
afile << "2";*/
}
afile << "\t\t" << "// Possible type " << i << endl;
}
然后我的 afile
在执行代码后看起来像这样:
8 // Possible type 1
1 7 // Possible type 2
4 0 3 // Possible type 3
当我从这个文件中读取数据并输出到另一个文件时出现问题:
int type;
while (afile >> type)
{
if(type == 0)
{
afile >> .........;
..........
}
else if(type == 1) {.........}
else if(type == 2) {.........}
}
}
......................................
我的输出文件在第一行后停止读取,因为它还读取了要处理的无效数据的注释,如果我删除注释,则一切正常。那么我该如何克服这种情况呢?谢谢。
有几种方法可以做到这一点。
找到引述后跳过一行的剩余部分(更快)
基本上你在这里要做的是在循环中逐行读取文件。当您点击两个字符“//”时。你会调用 "break;" 并跳到下一行。'
一些未经测试的虚拟代码:
while(line = file.getLine()){
loopChars = sizeof(line);
for(x = 0; x < loopChars; x++) {
char currentChar = line[x];
if(x+1 < loopChars){
char nextChar = line[x+1];
} else {
char nextChar = '';
}
if(nextChar == "/" && currentChar == "/"){
// Go to next line
break;
} else {
// Do your normal processing here
}
}
}
先删除引号(较慢)
这是一个从文件中删除引号(单行“//”和多行“/**/”)的解决方案。基本上,在开始读取数字数据之前,您会 运行 针对您正在处理的文件执行此操作。
http://www.cplusplus.com/forum/beginner/80380/
#include <iostream>
#include <fstream>
using namespace std;
int main (){
ifstream infile;
string filename;
ofstream outfile;
char c1, c2;
bool isInsideComment = false;
cout << "Please input file name (to remove comments from): ";
cin >> filename;
infile.open (filename.c_str());
if (infile.fail()) {
cout << "nInvaild file name.n";
return 1;
}
outfile.open(("out_"+filename).c_str());
infile.get (c1);
while (!infile.eof()) {
if ((c1 == '/') && (!isInsideComment)) {
infile.get (c2);
if (c2 == '*')
isInsideComment = true;
else if ((c1 == '/') && (c2 == '/'))
isInsideComment = true;
else {
outfile.put (c1);
outfile.put (c2);
}
}
else if ( (c1 == '*') && isInsideComment) {
infile.get (c2);
if (c2 == '/')
isInsideComment = false;
else if ((c1 == 'n') && isInsideComment)
isInsideComment = false;
}
else if (!isInsideComment)
outfile.put (c1);
infile.get (c1);
}
infile.close();
outfile.close();
}
您有几个合理的选择:
将整行读入
std::string
,扫描并删除任何评论,然后从剩下的任何内容创建一个std::istringstream
并从中提取非评论值在读取值之前,使用
>> std::ws
和afile.peek()
查看下一个字符是否是'/'
:如果是,请跳过,直到到达换行符。
前者是在 C++ 中习惯的有用技术(当您想报告带有数据问题的行号时会有所帮助),看起来像这样:
if (std::ifstream in(filename))
{
std::string line;
while (getline(in, line))
{
std::string::size_type n = line.find("//");
if (n != std::string::npos)
line.erase(n);
std::istringstream iss(line);
int atype;
while (iss >> atype)
...etc...
}
据我所知,Tony D 已经提供了一个合理的答案,但我想我也应该添加自己的代码,因为我已经编写并测试了它。
对于任何使用 C++ 的人来说,下面的内容几乎都是不言自明的,这基本上是 Tony 提出的,但有一点不同——逐行获取数据,利用 std::stringstream
、但随后也利用了 OP 使用的数据的二进制性质。那里的数据要么是一个有效的整数,要么是一条评论。或者换句话说,要么是有效整数,要么不是。所以在下面的代码中,当无法将数据从流有效转换为整数时 - 该行的其余部分被视为注释。 编辑:...实际上,虽然它是有点有效的解决方案,我修改了代码以纳入更明智的方法 - 一种跳过注释的方法(用 #
或 //
表示以显示两种方法)但仍然让我们决定要做什么在畸形值上。这不允许 45fubar
作为 45
传递,然后是错误的 fubar
,这是先前代码的问题,但允许 45//comment
被正确解释。
我仍然认为直接删除 \/\/.*?
是更好的方法。不过,这个答案的要点有点不同。 ;)
#include <ctime>
#include <cmath>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
void write(std::ostream& output, int lines) {
for (int i = 0; i < lines; i++) { // for i lines
int n = rand() % 10 + 1; // generate n numbers per line
for (int j = 0; j < n; j++) { // loop over line
output << rand() % 99; // output a random number
if (j + 1 < n) { // if not last?
output << "\t\t"; // then add tabs
}
}
output << " // " << n << " numbers\n"; // I'm not using std::endl here because it actually *flushes* the stream - flushing every iteration isn't advisable
}
}
std::vector<std::vector<int>> read(std::istream& input) {
std::vector<std::vector<int>> v; // a vector of vectors of ints
std::string line;
while (std::getline(input, line)) { // getline returns the stream by reference, so this handles EOF
std::stringstream ss(line); // create a stringstream out of line
int n = 0;
std::vector<int> numbers_in_line;
while (ss) { // while the stream is good
std::string word;
if (ss >> word) { // if there's still data to get
std::stringstream tester(word);
tester >> n;
if (tester && tester.peek() == std::char_traits<char>::eof()) { // conversion went well, no data was left in stream
numbers_in_line.push_back(n); // push it to the vector
} else { // conversion didn't go well, or went well but data was left in the stream
bool conversion_went_well = tester.good();
tester.clear();
char c = tester.get();
if (c == '#' || (c == '/' && tester.peek() == '/')) { // if it's a comment
if (conversion_went_well) {
numbers_in_line.push_back(n); // push it to the vector
}
break; // and ignore the rest of the line
} else {
std::cerr << "Unexpected symbol: '" << tester.str() << "'\n"; // report unexpected data
// so how do we handle a malformed value?
// error out? ignore following values in this line? accept following values in this line?
// if you leave it as is now - it will accept following values from this line
}
}
}
}
v.push_back(numbers_in_line);
}
return v;
}
int main(int argc, char** argv) {
std::srand(std::time(nullptr));
write(std::cout, 4); // write random data
std::vector<std::vector<int>> numbers = read(std::cin); // read the data
for (std::vector<int> line: numbers) { // loop over vector via C++11 features
for (int n: line) {
std::cerr << n << " ";
}
std::cerr << "\n";
}
return 0;
}
一个例子运行:
$ ./test.exe < data > data
50 44 92 43 97
26 32 54
30 91
93 4
$ cat data
50 44 92 43 97 // 5 numbers
26 32 54 // 3 numbers
30 91 // 2 numbers
93 4 // 2 numbers
$ ./test.exe < data2 > dump
Unexpected symbol: 'i91'
Unexpected symbol: '4i'
Unexpected symbol: 'lol'
Unexpected symbol: 'numbers'
50 44 92 43 97
26 32 54
30
93 3 2
7337
7337
$ cat data2
50 44 92 43 97 // 5 numbers
26 32 54 # 3 numbers
30 i91 // 2 numbers
93 4i lol 3 2 numbers
7337//test comment
7337#test comment 2