【问题标题】:Inconsistent behavior of std::regexstd::regex 的行为不一致
【发布时间】:2018-08-10 18:41:52
【问题描述】:

我有以下问题:

  • 如果我传递boost::filesystem::path::string() 的结果与将结果存储在中间字符串变量中,std::regex 的行为会有所不同。第一个将返回一个被截断的匹配,后来std::stoull 不接受该匹配(抛出 invalid_argument 异常),而第二个完美运行。

请参阅以下解释更多问题的命令:

[nix-shell:~]$ ls -l foo
total 0
-rw-r--r-- 1 amine users 0 Aug 10 16:55 008
-rw-r--r-- 1 amine users 0 Aug 10 15:47 2530047398992289207

[nix-shell:~]$ cat test-1.cpp
#include <iostream>
#include <regex>
#include <string>
#include <boost/filesystem.hpp>

int main() {
  std::regex expression{R"(([0-9]+))"};
  boost::filesystem::path cacheDir("/home/amine/foo");
  for (const auto& entry : boost::filesystem::directory_iterator{cacheDir})
  {
      std::smatch match;
      auto result = std::regex_match(entry.path().filename().string(), match, expression);
      std::cout << "Result: " << result << std::endl
        << "Length: " << match[1].length() << std::endl
        << "Match: " << match[1] << std::endl
        << "Filename: " << entry.path().filename().string() << std::endl
        << std::endl;

      std::stoull(match[1], 0);
  }
  return 0;
}
[nix-shell:~]$ g++ -o test1 test-1.cpp -lboost_filesystem -O0 -g

[nix-shell:~]$ ./test1
Result: 1
Length: 19
Match: 98992289207
Filename: 2530047398992289207

terminate called after throwing an instance of 'std::invalid_argument'
  what():  stoull
Aborted

[nix-shell:~]$ cat test-2.cpp
#include <iostream>
#include <regex>
#include <string>
#include <boost/filesystem.hpp>

int main() {
  std::regex expression{R"(([0-9]+))"};
  boost::filesystem::path cacheDir("/home/amine/foo");
  for (const auto& entry : boost::filesystem::directory_iterator{cacheDir})
  {
      std::smatch match;
      auto what = entry.path().filename().string();
      auto result = std::regex_match(what, match, expression);
      std::cout << "Result: " << result << std::endl
        << "Length: " << match[1].length() << std::endl
        << "Match: " << match[1] << std::endl
        << "Filename: " << entry.path().filename().string() << std::endl
        << std::endl;

      std::stoull(match[1], 0);
  }
  return 0;
}
[nix-shell:~]$ g++ -o test2 test-2.cpp -lboost_filesystem -O0 -g

[nix-shell:~]$ ./test2
Result: 1
Length: 19
Match: 2530047398992289207
Filename: 2530047398992289207

Result: 1
Length: 3
Match: 008
Filename: 008

所以我的问题是:

  • 为什么直接使用boost::filesystem::path::string()会截断std::regex的结果。
  • 假设匹配变量中的结果被截断也没问题,为什么std::stoull会抛出异常?

【问题讨论】:

  • 您使用的是哪个版本的 gcc?在 libstc++ 中 std::regex 的早期实现有缺陷
  • @AlanBirtles 我正在使用 gcc 7.3.0
  • 我喜欢制作一个由匹配函数返回的小助手结构,它将字符串和匹配器对象存储在一起。它有时可能效率低下,但使用起来很简单,我可以传入一个临时字符串。

标签: c++ boost std boost-filesystem


【解决方案1】:

不幸的是,你落入了一个陷阱。在 C++11 中,您调用的 std::regex_match 的重载是

template< class STraits, class SAlloc, 
          class Alloc, class CharT, class Traits >
bool regex_match( const std::basic_string<CharT,STraits,SAlloc>& s,
                  std::match_results<
                      typename std::basic_string<CharT,STraits,SAlloc>::const_iterator,
                      Alloc
                  >& m,
                  const std::basic_regex<CharT,Traits>& e,
                  std::regex_constants::match_flag_type flags = 
                      std::regex_constants::match_default );

因为它需要一个 const&amp; 到一个 std::string 你可以传递一个临时字符串。不幸的是,std::regex_match 不是为使用临时字符串而设计的。这就是为什么您会出现意外行为的原因。您尝试引用超出范围的数据。

C++14 通过添加解决了这个问题

template< class STraits, class SAlloc, 
          class Alloc, class CharT, class Traits >
bool regex_match( const std::basic_string<CharT,STraits,SAlloc>&&,
                  std::match_results<
                      typename std::basic_string<CharT,STraits,SAlloc>::const_iterator,
                      Alloc
                  >&,
                  const std::basic_regex<CharT,Traits>&,
                  std::regex_constants::match_flag_type flags = 
                      std::regex_constants::match_default ) = delete;

所以你不能再传递一个临时字符串。

如果您不能使用 C++14,那么您需要确保没有将临时字符串传递给 std::regex_match

【讨论】:

  • 似乎与g++ -o test1 test-1.cpp -lboost_filesystem -O0 -g -std=c++14 的结果相同?这不应该工作吗?
  • @AmineChikhoui 如果有的话,它应该无法在 C++14 中编译。
  • @AmineChikhoui 它应该无法编译。如果不符合,则该库不符合要求。
  • 原因是 match_result 对象只是将偏移量存储到字符串中,因此您需要使用字符串来查找实际的字符串。
猜你喜欢
  • 2017-12-19
  • 2012-11-11
  • 1970-01-01
  • 2016-10-11
  • 2019-07-20
  • 2016-10-04
  • 2018-09-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多