为什么我不能将此正则表达式返回字符串转换为整数或直接转换为整数? (C ++)

时间:2014-01-30 08:48:27

标签: c++ regex string char atoi

我已经阅读了一些关于将字符串转换为整数的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

运行该程序

这是一个较短的程序,问题更加明显:

5 个答案:

答案 0 :(得分:6)

你的问题在这一行:

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

您不能将临时文件作为正则表达式匹配的主题字符串传递,因为在查询匹配结果时,字符串内容仍然存在。 (string)matches[1]的结果在当前完整表达式结束时(即在下一个分号处)被销毁;当您在下一行查询time0[1]时,time0匹配指的是不再存在的字符串,这是未定义的行为。

答案 1 :(得分:3)

让我们通过一个例子来理解它:这就是我在VS2012环境中发生的事情:

enter image description here

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).

此时字符串必须存在且有效。使用调试器快速检查显示缺少某些内容:

enter image description here

缺少“ 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格式说明符可以轻松地捕获数据格式,并且比使用正则表达式更简单。请注意,您不必将数据拆分为标记,然后解析每个标记。您可以立即获得解析后的数字输出。