std::regex的行为不一致

3
我遇到了以下问题:
当我将`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存在缺陷。 - Alan Birtles
@AlanBirtles 我正在使用gcc 7.3.0。 - Amine Chikhaoui
我喜欢创建一个辅助结构体,由匹配函数返回,将字符串和匹配器对象一起存储。有时这可能效率不高,但它很容易使用,而且我可以传入临时字符串。 - xaxxon
1个回答

4
很抱歉,您已经陷入了困境。在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&格式的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 得到的结果相同,不应该可以工作吗? - Amine Chikhaoui
如果有什么问题,它应该在C++14中编译失败。 - melpomene
@AmineChikhaoui 应该无法编译通过。如果能够编译通过,那么这个库就不符合规范。 - NathanOliver
这是因为match_result对象仅存储字符串中的偏移量,因此您需要围绕该字符串以便查找实际字符串。 - xaxxon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接