我已经阅读了一些关于将字符串转换为整数的StackExchange帖子和其他页面,但这不起作用。这是我尝试的最后一件事:
if (infile.is_open())
{
while (getline (infile,line))
{
regex_match(line,matches,exp);
regex_match((string)matches[1], time0, exp_time);
buffer << time0[1];
str = buffer.str();
str.append("\0");
cout << atoi(str.c_str()) << '\n';
last_match = matches[2];
buffer.str(string());
}
infile.close();
}
我想不出任何其他方式。我尝试正常转换为字符串到char *到整数。我尝试将其转换为字符串,然后使用stoi()将其转换为整数。我尝试向它添加一个NULL字符(“\ 0”),我也尝试将它附加到缓冲区中。我也尝试了atof()和stof()。 stoi()和stof()都使程序崩溃。 atoi()和atof()都返回0,总是。
这是一个SSCCE,其中包含问题(atoi(str.c_str())
不应为0):
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <iostream>
#include <fstream>
#include <string>
#include <regex>
#include <sstream>
using namespace std;
int main(int argc, char* argv[])
{
regex exp("^(.+),(.+),.+,.+,(.+),.+,.+$");
regex exp_time("^(.+)-(.+)-(.+)");
smatch matches;
smatch time0;
string line;
ifstream infile(argv[1]);
string last_match;
stringstream buffer;
string str;
int i = 0;
if (infile.is_open())
{
while (getline(infile, line))
{
regex_match(line, matches, exp);
regex_match((string)matches[1], time0, exp_time);
buffer << time0[1];
str = buffer.str();
str = time0[1].str();
str.append("\0");
cout << atoi(str.c_str()) << " " << time0[1] << '\n';
last_match = matches[2];
buffer.str(string());
i++;
}
infile.close();
}
return 0;
}
输入将是具有以下值的csv文件:
1996-09-04,19.00,19.25,18.62,18.87,528000,0.79
1996-09-03,19.00,19.37,18.75,19.00,1012800,0.79
1996-08-30,19.87,20.12,19.37,19.62,913600,0.82
1996-08-29,20.87,21.12,19.75,19.75,1987200,0.82
1996-08-28,20.12,22.12,20.12,21.12,5193600,0.88
1996-08-27,19.75,20.37,19.75,20.12,1897600,0.84
1996-08-26,20.12,20.12,19.75,19.75,388800,0.82
1996-08-23,19.75,20.25,19.75,19.75,1024000,0.82
1996-08-22,18.62,20.00,18.25,19.87,1921600,0.83
1996-08-21,19.12,19.25,18.25,18.62,688000,0.78
1996-08-20,19.62,19.62,19.12,19.12,494400,0.80
1996-08-19,19.37,19.62,19.37,19.62,428800,0.82
1996-08-16,19.50,19.87,19.12,19.37,864000,0.81
您可以使用program.exe filename.csv
这是一个较短的程序,问题更加明显:
答案 0 :(得分:6)
你的问题在这一行:
regex_match((string)matches[1], time0, exp_time);
您不能将临时文件作为正则表达式匹配的主题字符串传递,因为在查询匹配结果时,字符串内容仍然存在。 (string)matches[1]
的结果在当前完整表达式结束时(即在下一个分号处)被销毁;当您在下一行查询time0[1]
时,time0
匹配指的是不再存在的字符串,这是未定义的行为。
答案 1 :(得分:3)
让我们通过一个例子来理解它:这就是我在VS2012环境中发生的事情:
buffer << time0[1];
行有错误。
在那行中,我实际上是在调用std::ostream::operator<< 通过传递std::match_results::operator[]的结果,这是一个 std :: sub_match对象引用。
该对象可以转换为string_type
(与迭代器类型引用的字符一起使用的basic_string
类型的别名),因为已为其定义了转换。
所以我正在做点什么:
buffer << (string with the contents of sub_match object).
此时字符串必须存在且有效。使用调试器快速检查显示缺少某些内容:
缺少“ first ”字段,该字段是匹配开头的迭代器。迭代器是bidirectional iterator pointing to your string:所以字符串必须发生的事情。
如果你看一下(再次,在VS2012环境中)如何定义 regex_match 函数:
template<class _StTraits,
class _StAlloc,
class _Alloc,
class _Elem,
class _RxTraits> inline
bool regex_match(
const basic_string<_Elem, _StTraits, _StAlloc>& _Str, <--- take a look here
match_results<typename basic_string<_Elem, _StTraits, _StAlloc>::
const_iterator, _Alloc>& _Matches,
const basic_regex<_Elem, _RxTraits>& _Re,
regex_constants::match_flag_type _Flgs =
regex_constants::match_default)
{ // try to match regular expression to target text
return (_Regex_match(_Str.begin(), _Str.end(),
&_Matches, _Re, _Flgs, true));
}
很明显,正在对一个const basic_string 进行引用,它是 NOT 以某种方式复制它,也不是为了摆弄它的r值。
您可以使用以下代码模拟相同的行为:
std::string::iterator myFirstElement; // every random-access iterator is a bidirectional iterator
void takeAReference(std::string& mystring)
{
// Here mystring is valid!
myFirstElement = mystring.begin();
}
int main(int argc, char* argv[])
{
takeAReference(string("hello dear"));
// Iterator is now NO MORE VALID! Try to inspect it / use it
....
}
亲自尝试一下。在我的机器上,这肯定不会起作用,即使它起作用,你也可以确定它迟早会让你失望。
这就是你得到奇怪结果的原因。一个简单的解决方案可能是扩展字符串的可见性范围:
int main(int argc, char* argv[])
{
regex exp("^(.+),(.+),.+,.+,(.+),.+,.+$");
regex exp_time("^(.+)-(.+)-(.+)");
smatch matches;
smatch time0;
string line;
ifstream infile("testfile.txt");
string last_match;
stringstream buffer;
string str;
int i = 0;
if (infile.is_open())
{
while (getline(infile, line))
{
regex_match(line, matches, exp);
std::string first_date = (string)matches[1]; <--!!
regex_match(first_date, time0, exp_time);
buffer << time0[1];
str = buffer.str();
str = time0[1].str();
str.append("\0");
cout << atoi(str.c_str()) << " " << time0[1] << '\n';
last_match = matches[2];
buffer.str(string());
i++;
}
infile.close();
}
return 0;
}
答案 2 :(得分:2)
你确定你的正则表达式符合你想要的吗?
例如,正则表达式"^(.+)-(.+)-(.+)$"
将匹配示例输入文件中的整行,例如它与whole line匹配:
1996-09-04,19.00,19.25,18.62,18.87,528000,0.79
因为.+
部分只会匹配任何内容(包括 - 字符等)。
因此,如果您只想匹配1996-09-04
,那么您可以尝试正则表达式\d{4}-\d{1,2}-\d{1,2}
或类似的东西。你可以试试这个online regex-tool
另一个正则表达式^(.+),(.+),.+,.+,(.+),.+,.+$
看起来很可疑,你真的要匹配任何行,其中有6个逗号,其间至少有1个字符吗?请记住,.
是一个非常贪婪的正则表达式。
更新:我认为您的第一个正则表达式过于贪婪,请参阅example here
答案 3 :(得分:0)
int atoi (const char * str);
尝试使用char数组而不是string
。
答案 4 :(得分:-1)
我认为可以在这里应用KISS原则来获得比使用正则表达式更好的解决方案。只需使用istream
在每个字段中阅读。 正则表达式过度恕我直言。
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
struct date_t
{
int year, month, day;
};
struct data_t
{
date_t date;
float f1, f2, f3, f4;
int i;
float f5;
};
istream & operator>>(istream & in, date_t &date)
{
char d1, d2; // dummy chars for the hyphens
return in >> date.year >> d1 >> date.month >> d2 >> date.day;
}
istream & operator>>(istream & in, data_t &data)
{
char d1, d2, d3, d4, d5, d6; // dummy chars for the commas
return in >> data.date >> d1 >> data.f1 >> d2 >> data.f2 >> d3
>> data.f3 >> d4 >> data.f4 >> d5 >> data.i >> d6 >> data.f5;
}
ostream & operator<<(ostream & out, const date_t &date)
{
return out << date.year << '-' << date.month << '-' << date.day;
}
ostream & operator<<(ostream & out, const data_t &data)
{
return out << data.date << ',' << data.f1 << ',' << data.f2 << ','
<< data.f3 << ',' << data.f4 << ',' << data.i << ',' << data.f5;
}
int main(int argc, char* argv[])
{
ifstream infile(argv[1]);
data_t data;
while(infile >> data) {
cout << "Here is the data: " << data << endl;
}
infile.close();
return 0;
}
哎呀,iostream
也有点矫枉过正。以下是使用fscanf
的C解决方案。
#include <stdio.h>
#include <stdio.h>
struct date_t
{
int year, month, day;
};
struct data_t
{
struct date_t date;
float f1, f2, f3, f4;
int i;
float f5;
};
int read_data(FILE *fid, struct data_t *data)
{
return fscanf(fid, "%d-%d-%d,%f,%f,%f,%f,%d,%f",
&(data->date.year), &(data->date.month), &(data->date.day),
&(data->f1), &(data->f2), &(data->f3), &(data->f4), &(data->i), &(data->f5));
}
int main(int argc, char* argv[])
{
FILE *fp = fopen(argv[1], "rt");
struct data_t data;
while(read_data(fp, &data) == 9) {
printf("Here is your data: %d-%02d-%02d,%.2f,%.2f,%.2f,%.2f,%d,%.2f\n",
data.date.year, data.date.month, data.date.day,
data.f1, data.f2, data.f3, data.f4, data.i, data.f5);
}
return 0;
}
看看它有多短,容易理解? scanf
格式说明符可以轻松地捕获数据格式,并且比使用正则表达式更简单。请注意,您不必将数据拆分为标记,然后解析每个标记。您可以立即获得解析后的数字输出。