如何使用 C++/Boost 过滤字符串中的字符

Posted

技术标签:

【中文标题】如何使用 C++/Boost 过滤字符串中的字符【英文标题】:How to filter characters from a string with C++/Boost 【发布时间】:2010-07-08 17:29:44 【问题描述】:

这似乎是一个非常基本的问题,所以如果它已经在某个地方得到回答,我深表歉意(我的搜索没有找到任何东西)。

我只想过滤一个字符串对象,使其只包含字母数字和空格字符。

这是我尝试过的:

#include "boost/algorithm/string/erase.hpp"
#include "boost/algorithm/string/classification.hpp"

std::wstring oldStr = "Bla=bla =&*\nSampleSampleSample ";
std::wstring newStr = boost::erase_all_copy(oldStr, !(boost::is_alnum() || 
                                                      boost::is_space()));

但是编译器对此一点也不满意——看来我只能在erase_all_copy 的第二个参数中放入一个字符串,而不是is_alnum() 的东西。

我在这里缺少一些明显的解决方案吗?

【问题讨论】:

【参考方案1】:

使用 std 算法和 Boost.Bind:

std::wstring s = ...
std::wstring new_s;
std::locale loc;
std::remove_copy_if(s.begin(), s.end(), std::back_inserter(new_s), 
    !(boost::bind(&std::isalnum<wchar_t>, _1, loc)||
      boost::bind(&std::isspace<wchar_t>, _1, loc)
));

【讨论】:

成功了,谢谢!在这一点上,这一切对我来说都像是黑魔法,但它会给我一个更好地理解迭代器和 C++ 方式的地方。 @jjiffer: remove_copy_if 接受一个输入范围(“s.begin(), s.end()”部分)和一个输出迭代器,它将提取的字符写入其中(“back_inserter(new_s )“ 部分)。第四个参数是一个函数对象,它接受一个元素作为输入(在本例中为 wchar_t)并返回 bool。如果此函数返回 true,则跳过该元素。 (待续......) 这个函数对象是在这里用 boost::bind 创建的。 "bind(&isalnum, _1, loc)" 返回一个函数对象,该对象存储指向 isalnum 函数的指针和 "loc" 的副本。该函数对象在使用一个参数调用时将依次调用 isalnum(the_argument, loc) 并返回其结果。最后,运算符重载魔法允许我们将使用绑定创建的两个函数对象与 OR 运算符组合,并使用 NOT 运算符对其结果求反。 啊,我明白了。我不知道 C++ 有任何类似绑定的东西。谢谢解释!【参考方案2】:

我使用 boost 已经有好几年了,但也许您可以使用 erase_all_regex_copy() 而不是 erase_all_copy()?这可能会影响性能,但除了迭代每个元素并手动检查之外,它可能是您唯一的选择。如果您不熟悉正则表达式,在这种情况下您将使用的表达式类似于“[^a-zA-Z0-9 ]+”。

为了完整起见,一些示例代码:

#include "boost/regex.hpp"
#include "boost/algorithm/string/regex.hpp"

std::wstring oldStr = "Bla=bla =&*\nSampleSampleSample ";
std::wstring newStr = boost::erase_all_regex_copy(oldStr, boost::regex("[^a-zA-Z0-9 ]+"));

【讨论】:

我收到有关 char_t 和 wchar_t 转换的错误。也许这个正则表达式隐含地假设 char_t 而不是 wchar_t?我在正则表达式字符串前面放了一个 L,但它也不喜欢这样。 long long 错误消息的开头: C:\boost_1_40_0\boost/regex/v4/perl_matcher_common.hpp(802) : warning C4244: 'argument' : conversion from 'const wchar_t' to 'char', possible loss of data 试试把 boost::regex("etc.") 改成 boost::regex("etc.") 吧?再说一次,Eric 有一个很好的解决方案,可能不会给你带来同样的麻烦。 :)(另外,我会自己测试这段代码,但我的开发机器上没有安装 boost...现在正在构建/安装它。)【参考方案3】:

对于那些不太聪明的人,这里是基于@Éric Malenfant 回答的 ANSI 和 UNICODE 函数:

std::string CleanString(const std::string& Input)

    std::string clean_string;
    std::locale loc;

    try 
        std::remove_copy_if(Input.begin(), Input.end(), std::back_inserter(clean_string),
            !(boost::bind(&std::isalnum<unsigned char>, _1, loc) || boost::bind(&std::isspace<unsigned char>, _1, loc)
        ));
    
    catch (const std::bad_alloc& e) 
        std::cout << "Allocation failed: " << e.what() << '\n';
    

    return clean_string;


std::wstring CleanString(const std::wstring& Input)

    std::wstring clean_string;
    std::locale loc;

    try 
        std::remove_copy_if(Input.begin(), Input.end(), std::back_inserter(clean_string),
            !(boost::bind(&std::isalnum<wchar_t>, _1, loc) ||
              boost::bind(&std::isspace<wchar_t>, _1, loc)
        ));
     catch (const std::bad_alloc& e) 
        std::cout << "Allocation failed: " << e.what() << '\n';
    
    return clean_string;

在线演示:https://wandbox.org/permlink/MFTwXV4ZCi9nsdlC

Linux 的完整测试代码:

#include <iostream>
#include <algorithm>
#include <cctype>
#include <boost/bind.hpp>

// Note on Linux we use char and not unsigned char!
std::string CleanString(const std::string& Input)

    std::string clean_string;
    std::locale loc;

    try 
        std::remove_copy_if(Input.begin(), Input.end(), std::back_inserter(clean_string),
            !(boost::bind(&std::isalnum<char>, _1, loc) || boost::bind(&std::isspace<char>, _1, loc)
        ));
    
    catch (const std::bad_alloc& e) 
        std::cout << "Allocation failed: " << e.what() << '\n';
     
    catch (...)
    
    

    return clean_string;


std::wstring CleanString(const std::wstring& Input)

    std::wstring clean_string;
    std::locale loc;

    try 
        std::remove_copy_if(Input.begin(), Input.end(), std::back_inserter(clean_string),
            !(boost::bind(&std::isalnum<wchar_t>, _1, loc) ||
              boost::bind(&std::isspace<wchar_t>, _1, loc)
        ));
     
    catch (const std::bad_alloc& e) 
        std::cout << "Allocation failed: " << e.what() << '\n';
    
    catch (...)
    
    

    return clean_string;


int main()

    std::string test_1 = "Bla=bla =&*\n Sample Sample Sample !$%^&*@~";

    std::string new_test_1 = CleanString(test_1);

    if (!new_test_1.empty())
    
        std::cout << "ANSI: " << new_test_1 << std::endl;
    

    std::wstring test_uc_1 = L"!$%^&*@~ test &*";

    std::wstring new_test_uc_1 = CleanString(test_uc_1);

    if (!new_test_uc_1.empty())
    
        std::wcout << L"UNICODE: " << new_test_uc_1 << std::endl;
    

    return 0;

感谢 Éric Malenfant。

【讨论】:

以上是关于如何使用 C++/Boost 过滤字符串中的字符的主要内容,如果未能解决你的问题,请参考以下文章

boost库之字符串格式化

Regex:过滤特殊字符(如日语),但保留表情符号

如何用js或则jquery过滤特殊字符

根据列R / dplyr中的“复杂”字符串过滤行

如何使用 boost::asio 将 URL 转换为 IP 地址?

一起学习Boost标准库--Boost.texical_cast&format库