std::regex 的不一致行为
Inconsistent behavior of std::regex
我遇到以下问题:
如果我传递 boost::filesystem::path::string()
的结果与将结果存储在中间字符串变量中,std::regex
的行为会有所不同。第一个将 return 匹配被截断并且后来不被 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
会抛出异常?
你很不幸掉进了陷阱。在 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
我遇到以下问题:
-
如果我传递
std::regex
的行为会有所不同。第一个将 return 匹配被截断并且后来不被std::stoull
接受(抛出 invalid_argument 异常),而第二个工作完美。
boost::filesystem::path::string()
的结果与将结果存储在中间字符串变量中,请参阅以下命令以进一步解释问题:
[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
会抛出异常?
你很不幸掉进了陷阱。在 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