boost::locale::transform 的可移植使用
Posted
技术标签:
【中文标题】boost::locale::transform 的可移植使用【英文标题】:portable usage of boost::locale::transform 【发布时间】:2014-11-20 14:39:10 【问题描述】:我实现了对字符串中的子字符串的搜索,我想让这个搜索“重音自然”或者它可能被称为粗糙 - 如果我在“rábano”中开始搜索“aba”,我应该会成功。
在Find substring in string using locale 中有一个有效的答案:
#include <locale>
#include <string>
#include <boost/locale.hpp>
std::string NormalizeString(const std::string & input)
std::locale loc = boost::locale::generator()("");
const boost::locale::collator<char>& collator = std::use_facet<boost::locale::collator<char> >(loc);
std::string result = collator.transform(boost::locale::collator_base::primary, input);
return result;
此解决方案的唯一问题 - 转换将几个字节添加到字符串的末尾。在我的情况下,它是“\x1\x1\x1\x1\x0\x0\x0”。四个字节,一个 1 和几个零字节。 当然,擦除这些字节很容易,但我不想依赖这种微妙的实现细节。 (代码应该是跨平台的)
有没有更可靠的方法?
【问题讨论】:
transform
的文档不保证对子字符串搜索有用。它是完全便携的;它恰好是您想要的错误工具。排序规则是排序功能,而不是搜索。 AFAIK Boost.Locale 不提供搜索功能。
是不是意味着只有ICU才有帮助?
【参考方案1】:
作为@R。 Martinho Fernandes 表示,通过 boost 来实施这样的搜索似乎是不可能的。 我在 chrome 资源中找到了解决方案。它使用ICU。
// This class is for speeding up multiple StringSearchIgnoringCaseAndAccents()
// with the same |find_this| argument. |find_this| is passed as the constructor
// argument, and precomputation for searching is done only at that timing.
class CStringSearchIgnoringCaseAndAccents
public:
explicit CStringSearchIgnoringCaseAndAccents(std::u16string find_this);
~CStringSearchIgnoringCaseAndAccents();
// Returns true if |in_this| contains |find_this|. If |match_index| or
// |match_length| are non-NULL, they are assigned the start position and total
// length of the match.
bool SearchIn(const std::u16string& in_this, size_t* match_index = nullptr, size_t* match_length = nullptr);
private:
std::u16string _find_this;
UStringSearch* _search_handle;
;
CStringSearchIgnoringCaseAndAccents::CStringSearchIgnoringCaseAndAccents(std::u16string find_this) :
_find_this(std::move(find_this)),
_search_handle(nullptr)
// usearch_open requires a valid string argument to be searched, even if we
// want to set it by usearch_setText afterwards. So, supplying a dummy text.
const std::u16string& dummy = _find_this;
UErrorCode status = U_ZERO_ERROR;
_search_handle = usearch_open((const UChar*)_find_this.data(), _find_this.size(),
(const UChar*)dummy.data(), dummy.size(), uloc_getDefault(), NULL, &status);
if (U_SUCCESS(status))
UCollator* collator = usearch_getCollator(_search_handle);
ucol_setStrength(collator, UCOL_PRIMARY);
usearch_reset(_search_handle);
CStringSearchIgnoringCaseAndAccents::~CStringSearchIgnoringCaseAndAccents()
if (_search_handle) usearch_close(_search_handle);
bool CStringSearchIgnoringCaseAndAccents::SearchIn(const std::u16string& in_this, size_t* match_index, size_t* match_length)
UErrorCode status = U_ZERO_ERROR;
usearch_setText(_search_handle, (const UChar*) in_this.data(), in_this.size(), &status);
// Default to basic substring search if usearch fails. According to
// http://icu-project.org/apiref/icu4c/usearch_8h.html, usearch_open will fail
// if either |find_this| or |in_this| are empty. In either case basic
// substring search will give the correct return value.
if (!U_SUCCESS(status))
size_t index = in_this.find(_find_this);
if (index == std::u16string::npos)
return false;
else
if (match_index)
*match_index = index;
if (match_length)
*match_length = _find_this.size();
return true;
int32_t index = usearch_first(_search_handle, &status);
if (!U_SUCCESS(status) || index == USEARCH_DONE) return false;
if (match_index)
*match_index = static_cast<size_t>(index);
if (match_length)
*match_length = static_cast<size_t>(usearch_getMatchedLength(_search_handle));
return true;
用法:
CStringSearchIgnoringCaseAndAccents searcher(a_utf16_string_what.c_str()));
searcher.SearchIn(a_utf16_string_where)
【讨论】:
【参考方案2】:尽管这是一个老问题,但我还是决定发布我的解决方案,因为它可能会对某人有所帮助(或者如果我错了,有人可以告诉我)。我使用了boost text conversion methods。首先我应用了normalization form decomposition (NFD),它给了我分隔的字符。然后我只是过滤了那些代码低于 255 的代码。然后是一个简单的小写转换。它适用于您的问题(也适用于我的问题),但我不确定它是否适用于所有情况。这是解决方案:
#include <iostream>
#include <algorithm>
#include <string>
#include <locale>
#include <boost/locale.hpp>
static std::locale loc = boost::locale::generator()("en_US.UTF-8");
std::string NormalizeString(const std::string & input)
std::string s_norm = boost::locale::normalize(input, boost::locale::norm_nfd, loc);
std::string s;
std::copy_if(s_norm.begin(), s_norm.end(), std::back_inserter(s), [](unsigned int ch)return ch<256; );
return boost::locale::to_lower(s, loc);
void find_norm(const std::string& input, const std::string& query)
if (NormalizeString(input).find(NormalizeString(query)) != std::string::npos)
std::cout << query << " found in " << input << std::endl;
else
std::cout << query << " not found in " << input << std::endl;
int main(int argc, char *argv[])
find_norm("rábano", "aba");
find_norm("rábano", "aaa");
return EXIT_SUCCESS;
【讨论】:
以上是关于boost::locale::transform 的可移植使用的主要内容,如果未能解决你的问题,请参考以下文章