【问题标题】:Why can I not convert this regex return to a string to an integer or straight to an integer? (C++)为什么我不能将此正则表达式返回字符串转换为整数或直接转换为整数? (C++)
【发布时间】:2014-02-22 11:10:15
【问题描述】:

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

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

【问题讨论】:

  • 您使用的是哪种输入字符串和正则表达式?你确定你得到一个类似于数字的字符串吗?
  • 请写一个显示所有定义的简单测试用例
  • 您是否验证过正则表达式确实匹配并且结果中的第二项存在?
  • time0matches 有什么类型?此外,提供SSCCE 总是好的。
  • 我添加了一个 SSCCE 和一些示例输入。

标签: c++ regex string char atoi


【解决方案1】:

int atoi (const char * str);

尝试使用 char 数组而不是 string

【讨论】:

  • string::c_str 返回char *
  • 好点!在实际的 atoi 呼叫之前,我到处寻找。哎呀!
【解决方案2】:

你的问题出在这一行:

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

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

【讨论】:

  • 我不确定我是否理解。它一直工作到 atoi() 或 atof(),之后,它将匹配更改为 0。
  • 未定义的行为并不意味着它必须立即崩溃。这意味着任何事情都可能发生。在主题字符串消失后从正则表达式匹配中读取可能会导致程序崩溃,或者它可能会给您带来垃圾,或者如果内存尚未回收,它可能会给您带来预期的结果。我不能说你的特定编译器在做什么,但我的猜测是 time0[1] 包含垃圾,并且 atoi() 返回 0,因为它没有被提供一个有效的整数。
  • 哇,这很奏效,听起来似乎是一个合理的理由。很有意思。我对临时工一无所知。我猜这实际上是与数据类型相关联的函数的返回。如何奖励赏金?
  • 你太晚了,无法奖励赏金。一半的赏金会自动分配给我,因为我在到期时得到了最高评分。
【解决方案3】:

您确定您的正则表达式与您想要的匹配吗?

例如,正则表达式 "^(.+)-(.+)-(.+)$" 将匹配示例输入文件中的整行,例如它匹配 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中的正则表达式

另外,另一个正则表达式 ^(.+),(.+),.+,.+,(.+),.+,.+$ 在我看来很可疑,您真的要匹配 any 行,其中包含 6 个逗号且它们之间至少有 1 个字符?请记住,. 是一个非常贪婪的正则表达式。

更新:我真的觉得你的第一个正则表达式太贪心了,见example here

【讨论】:

  • 是的,正则表达式完美匹配所有内容。在atoi()cout &lt;&lt; atoi(str.c_str()) &lt;&lt; " " &lt;&lt; time0[1] &lt;&lt; '\n';atoi() 之前,代码都是完美的
  • @JVE999 donfuxx 是正确的,正则表达式并没有真正做到你认为它正在做的事情。它恰好适用于您的输入,但如果您将其替换为虚假输入(例如,-----),则正则表达式仍将匹配,并且当您尝试使用 atoi 解析时,您将得到 0 没有任何错误指示。此外,. 并不是唯一贪婪的东西——+ 也是。因此,如果输入是foo-bar-biz-baz,仍然会发生匹配,其中一个匹配将包含-
  • 当时我想不出更好的方法。现在,我发现([^,]+)([^-]+) 更简单,而([^,]*)([^-]*) 可能会使其更加通用。
【解决方案4】:

我认为可以在这里应用 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 格式说明符可以轻松捕获数据的格式,而且它比正则表达式更易于使用。请注意,您不必将数据拆分为标记,然后解析每个标记。您会立即获得解析后的数字输出。

【讨论】:

  • 请注意,scanf 版本实际上比 C++ 版本做得更多。 scanf 实际上会检查分隔符(连字符/逗号)。而 C++ 版本会毫无怨言地接受各种无效数据。
【解决方案5】:

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

buffer &lt;&lt; time0[1]; 行中有错误。

在那一行中,我实际上是在调用std::ostream::operator<< 通过将std::match_results::operator[] 的结果传递给它,这是一个std::sub_match 对象引用

该对象可以转换为 string_typebasic_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 的引用,它不是以某种方式复制它,也不是 r-value 摆弄它。

您可以使用以下代码模拟相同的行为:

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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-09
    • 2013-03-10
    • 1970-01-01
    • 2017-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-18
    相关资源
    最近更新 更多