Почему я не могу преобразовать этот возврат регулярного выражения в строку в целое число или прямо в целое число? (С++)

Я прочитал несколько сообщений 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

Вот более короткая программа с более очевидными проблемами:


person JVE999    schedule 30.01.2014    source источник
comment
какие входные строки и регулярные выражения вы используете? вы уверены, что получаете строку, напоминающую число?   -  person BRPocock    schedule 30.01.2014
comment
Пожалуйста, напишите простой тестовый пример, показывающий все определения   -  person    schedule 30.01.2014
comment
Вы проверили, что регулярные выражения действительно совпадают и что второй элемент в результатах существует?   -  person molbdnilo    schedule 30.01.2014
comment
Какой тип у time0 и matches? Также всегда полезно предоставить SSCCE.   -  person TobiMcNamobi    schedule 30.01.2014
comment
Я добавил SSCCE и несколько примеров ввода.   -  person JVE999    schedule 03.02.2014
comment
Это не совсем SSCCE. Программа делает три вещи (сопоставляет строку с RE, сопоставляет другую строку с другим RE, преобразует еще одну строку в целое число), и никогда не известно, какая из трех операций ошибочна. Используйте свой отладчик или вставьте операторы вывода, чтобы выяснить это. Изолируйте и отладьте эту операцию. Затем, если у вас все еще есть проблема, создайте SSCCE только вокруг этой операции. Если две или все три операции работают по отдельности, но не вместе, вы обязаны продемонстрировать это.   -  person n. 1.8e9-where's-my-share m.    schedule 03.02.2014
comment
Виновата часть atoi(). До этого все работает идеально. После конвертации получаю 0.   -  person JVE999    schedule 04.02.2014
comment
(1) Не используйте atoi(...); использовать boost::lexical_cast<int>(); (2) Не используйте приведения в стиле C [(string)];   -  person kingtorus    schedule 12.02.2014
comment
(3) regex_match принимает строковый параметр по постоянной ссылке. Приведение его создает временное, которое может исчезнуть при возврате вызова функции; (4) См. здесь небольшое изложение того, как использовать функцию str() для типа совпадения (а также итератор совпадений) regex; вы можете использовать cmatch вместо smatch, если хотите использовать c-строки старого стиля;   -  person kingtorus    schedule 12.02.2014


Ответы (5)


Ваша проблема в этой строке:

regex_match((string)matches[1], time0, exp_time);

Вы не можете передать временное значение в качестве строки темы совпадения с регулярным выражением, потому что содержимое строки все еще должно быть в наличии, когда вы запрашиваете результаты совпадения. Результат (string)matches[1] уничтожается в конце текущего полного выражения (т.е. в следующей точке с запятой); когда вы добираетесь до запроса time0[1] в следующей строке, совпадение time0 относится к строке, которой больше не существует, что является неопределенным поведением.

person Ross Smith    schedule 06.02.2014
comment
Я не уверен, что понимаю. Он работает до atoi() или atof(), после чего он меняет совпадение на 0. - person JVE999; 11.02.2014
comment
Неопределенное поведение не означает, что он должен немедленно рухнуть. Это означает, что все может случиться. Чтение из совпадения с регулярным выражением после того, как строка темы исчезла, может привести к сбою программы, или может дать вам мусор, или может дать вам ожидаемый результат, если память еще не была переработана. Я не могу сказать, что делает ваш конкретный компилятор, но я предполагаю, что time0[1] содержит мусор, а atoi() возвращает 0, потому что ему не передается допустимое целое число. - person Ross Smith; 12.02.2014
comment
Вау, это сработало, и это звучит как правдоподобная причина. Очень интересно. Я не знал этого о временных. Я предполагаю, что это действительно возврат функции, связанной с типом данных. Как вознаградить награду? - person JVE999; 14.02.2014
comment
Вы оставили это слишком поздно, чтобы присудить награду. Половина награды была автоматически назначена мне, потому что у меня был самый высокий рейтинг ответа, когда он истек. - person Ross Smith; 14.02.2014

Давайте разберемся на примере: вот что происходит в моей среде 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", которое является итератором начала совпадения, отсутствует. Этот итератор является двунаправленным итератором, указывающим на вашу строку: поэтому что-то должно произошло с вашей строкой.

Если вы посмотрите, как (опять же, в среде 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;
}
person Marco A.    schedule 12.02.2014

Вы уверены, что ваше регулярное выражение соответствует тому, что вы хотите?

например, регулярное выражение "^(.+)-(.+)-(.+)$" будет соответствовать всей строке в вашем примере входного файла, например, оно соответствует вся строка:

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 символ между ними? Помните, что . — очень жадное регулярное выражение.

ОБНОВЛЕНИЕ: я действительно думаю, что ваше первое регулярное выражение слишком жадное, см. пример здесь

person donfuxx    schedule 10.02.2014
comment
Да, регулярное выражение идеально подходит для всего. Код идеален до тех пор, пока atoi() в cout << atoi(str.c_str()) << " " << time0[1] << '\n'; - person JVE999; 11.02.2014
comment
@ JVE999 donfuxx верен, регулярное выражение на самом деле не делает то, что вы думаете. Бывает, что он работает с вашим вводом, но если вы заменили его фиктивным вводом (например, -----), регулярное выражение все равно будет соответствовать, и вы получите 0 при попытке проанализировать с atoi без каких-либо указаний на ошибку . Кроме того, не только . является жадным, но и +. Таким образом, если ввод был foo-bar-biz-baz, совпадение все равно произойдет, и одно из совпадений будет содержать -. - person Nicu Stiurca; 12.02.2014
comment
В то время я не мог придумать лучшего способа сделать это. Теперь я обнаружил, что ([^,]+) или ([^-]+) намного проще, а ([^,]*) и ([^-]*) могут сделать его более универсальным. - person JVE999; 14.02.2014

int atoi (const char * str);

Попробуйте использовать массив символов вместо string.

person adamdc78    schedule 06.02.2014
comment
string::c_str возвращает char * - person Matt; 07.02.2014
comment
Хорошая точка зрения! Я посмотрел везде до фактического звонка atoi. Упс! - person adamdc78; 07.02.2014

Я думаю, что здесь можно применить принцип KISS, чтобы получить лучшее решение, чем использование регулярных выражений. Просто прочитайте в каждом поле, используя istream. Regex — это излишество ИМХО.

#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 может легко фиксировать формат ваших данных, и его гораздо проще использовать, чем регулярное выражение. Обратите внимание, что вам не нужно разбивать данные на токены, а затем анализировать каждый токен. Вы сразу же получаете проанализированный числовой вывод.

person Nicu Stiurca    schedule 12.02.2014
comment
Обратите внимание, что версия scanf на самом деле делает больше, чем версия C++. Scanf фактически проверит разделители (дефисы/запятые). тогда как версия C++ будет принимать все виды недопустимых данных без жалоб. - person kleptog; 16.07.2018