Я прочитал несколько сообщений 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
Вот более короткая программа с более очевидными проблемами:
Ваша проблема в этой строке:
regex_match((string)matches[1], time0, exp_time);
Вы не можете передать временную строку в качестве соответствия теме регулярного выражения, потому что содержимое строки все еще должно присутствовать при запросе результатов поиска. Результат (string)matches[1]
уничтожается в конце текущего полного выражения (то есть в следующей точке с запятой); когда вы получите время для запросов time0[1]
на следующей строке time0
match ссылается на строку, которая больше не существует, что является неопределенным поведением.
Давайте разберемся с этим на примере: вот что происходит в моей среде VS2012:
Там есть ошибка в buffer << time0[1];
линия.
В этой строке я на самом деле называю станд :: ostream :: оператор<<
передавая его результат СТД :: match_results :: оператор [] который является ссылка на объект std :: sub_match.
Этот объект может быть преобразован в string_type
(псевдоним basic_string
используется для символов, на которые ссылается тип итератора), поскольку для него определено преобразование.
Итак, я делаю что-то:
buffer << (string with the contents of sub_match object).
В таком случае строка должна существовать и быть действительной. Быстрая проверка с помощью отладчика показывает, что чего-то не хватает:
«первый«поле, которое является итератор к началу матча, пропал, отсутствует. Этот итератор является двунаправленный итератор, указывающий на вашу строку: так должно быть, что-то случилось с вашей строкой.
Если вы посмотрите, как (опять же, в среде 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, его НЕ копировать его как-то, ни возиться с этим.
Вы можете симулировать то же поведение с помощью следующего кода:
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;
}
Вы уверены, что ваше регулярное выражение соответствует тому, что вы хотите?
например, регулярное выражение "^(.+)-(.+)-(.+)$"
будет соответствовать всей строке в вашем входном файле примера, как, например, он соответствует вся линия:
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}
или что-то типа того. Вы можете попробовать регулярное выражение в этом онлайн-инструмент регулярных выражений
Также другой регулярное выражение ^(.+),(.+),.+,.+,(.+),.+,.+$
выглядит подозрительно для меня, действительно ли вы хотите соответствовать любой строка, которая имеет 6 запятых, по крайней мере 1 символ между ними? Помните, что .
очень жадное регулярное выражение
ОБНОВИТЬ: Я действительно думаю, что ваш первый регулярный выражение слишком жадный, понимаете пример здесь
int atoi (const char * str);
Попробуйте использовать массив символов вместо string
,
Я думаю, что принцип 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
тоже немного перебор. Вот решение C с использованием fscanf
,
#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
спецификатор формата может без труда захватить формат ваших данных, и это гораздо проще в использовании, чем регулярное выражение. Обратите внимание, что вам не нужно разбивать данные на токены, а затем анализировать каждый токен. Вы сразу получаете анализируемый числовой вывод.