如何在 c++ 中从字符串中删除所有非字母数字字符
How to strip all non alphanumeric characters from a string in c++?
我正在编写一个软件,它要求我使用 libcurl 处理从网页获得的数据。当我获得数据时,由于某种原因,它有额外的换行符。我需要找到一种方法只允许字母、数字和空格。并删除其他所有内容,包括换行符。有什么简单的方法可以做到这一点吗?谢谢。
编写一个函数,该函数采用char
并返回true
,如果要删除该字符,则返回false
,如果要保留该字符:
bool my_predicate(char c);
然后使用 std::remove_if
算法从字符串中删除不需要的字符:
std::string s = "my data";
s.erase(std::remove_if(s.begin(), s.end(), my_predicate), s.end());
根据您的要求,您可以使用标准库谓词之一,例如 std::isalnum
,而不是编写自己的谓词(您说需要匹配字母数字字符和空格,所以这可能并不完全符合您的需求(。
如果要使用标准库std::isalnum
函数,则需要一个强制转换来消除 C 标准库标头<cctype>
中的std::isalnum
函数(这是要使用的函数(与 C++ 标准库标头<locale>
中的std::isalnum
(这不是要使用的函数, 除非您要执行特定于区域设置的字符串处理(:
s.erase(std::remove_if(s.begin(), s.end(), (int(*)(int))std::isalnum), s.end());
这同样适用于任何序列容器(包括std::string
、std::vector
和std::deque
(。 这个成语通常被称为"擦除/删除"成语。 std::remove_if
算法也适用于普通数组。 std::remove_if
只对序列进行一次传递,因此它具有线性时间复杂度。
以前对 std::isalnum
的用法不会在不传递一元参数的情况下用 std::ptr_fun
进行编译,因此这个带有 lambda 函数的解决方案应该封装正确答案:
s.erase(std::remove_if(s.begin(), s.end(),
[]( auto const& c ) -> bool { return !std::isalnum(c); } ), s.end());
使用 string
,您可以随时循环并只erase
所有非字母数字字符。
#include <cctype>
size_t i = 0;
size_t len = str.length();
while(i < len){
if (!isalnum(str[i]) || str[i] == ' '){
str.erase(i,1);
len--;
}else
i++;
}
使用标准库更好的人可能可以在没有循环的情况下做到这一点。
如果仅使用char
缓冲区,则可以循环访问,如果字符不是字母数字,请将所有字符向后移动(以覆盖有问题的字符(:
#include <cctype>
size_t buflen = something;
for (size_t i = 0; i < buflen; ++i)
if (!isalnum(buf[i]) || buf[i] != ' ')
memcpy(buf[i], buf[i + 1], --buflen - i);
只是将James McNellis的代码扩展了一点。他的功能是删除 alnum 字符而不是非 alnum 字符。
从字符串中删除非 alnum 字符。(alnum = 字母或数字(
-
声明一个函数(如果传递的字符不是 alnum,则 isalnum 返回 0(
bool isNotAlnum(char c) { return isalnum(c) == 0; }
-
然后写这个
s.erase(remove_if(s.begin(), s.end(), isNotAlnum), s.end());
那么你的字符串只有 alnum 字符。
对不同的方法进行基准测试。
如果你正在寻找一个基准,我做了一个。
(115830 cycles) 115.8ms -> using stringstream
( 40434 cycles) 40.4ms -> s.erase(std::remove_if(s.begin(), s.end(), [](char c) { return !isalnum(c); }), s.end());
( 40389 cycles) 40.4ms -> s.erase(std::remove_if(s.begin(), s.end(), [](char c) { return ispunct(c); }), s.end());
( 42386 cycles) 42.4ms -> s.erase(remove_if(s.begin(), s.end(), not1(ptr_fun( (int(*)(int))isalnum ))), s.end());
( 42969 cycles) 43.0ms -> s.erase(remove_if(s.begin(), s.end(), []( auto const& c ) -> bool { return !isalnum(c); } ), s.end());
( 44829 cycles) 44.8ms -> alnum_from_libc(s) see below
( 24505 cycles) 24.5ms -> Puzzled? My method, see below
( 9717 cycles) 9.7ms -> using mask and bitwise operators
Original length: 8286208, current len with alnum only: 5822471
<小时 />- Stringstream给出了可怕的结果(但我们都知道(
- 已经给出的不同答案给出了大约相同的运行时
- 以 C 方式始终如一地提供更好的运行时(几乎快两倍!(,这绝对值得考虑,最重要的是它与 C 语言兼容。
- 我的按位方法(也兼容 C(快了 400% 以上。
注意,必须修改所选答案,因为它只保留特殊字符
注意2:测试文件是一个(几乎(8192 kb的文本文件,大约有62个alnum和12个特殊字符,随机均匀地写入。
<小时 />基准测试源代码
#include <ctime>
#include <iostream>
#include <sstream>
#include <string>
#include <algorithm>
#include <locale> // ispunct
#include <cctype>
#include <fstream> // read file
#include <streambuf>
#include <sys/stat.h> // check if file exist
#include <cstring>
using namespace std;
bool exist(const char *name)
{
struct stat buffer;
return !stat(name, &buffer);
}
constexpr int SIZE = 8092 * 1024;
void keep_alnum(string &s) {
stringstream ss;
int i = 0;
for (i = 0; i < SIZE; i++)
if (isalnum(s[i]))
ss << s[i];
s = ss.str();
}
/* my method, best runtime */
void old_school(char *s) {
int n = 0;
for (int i = 0; i < SIZE; i++) {
unsigned char c = s[i] - 0x30; // '0'
if (c < 10 || (c -= 0x11) < 26 || (c -= 0x20) < 26) // 0x30 + 0x11 = 'A' + 0x20 = 'a'
s[n++] = s[i];
}
s[n] = ' ';
}
void alnum_from_libc(char *s) {
int n = 0;
for (int i = 0; i < SIZE; i++) {
if (isalnum(s[i]))
s[n++] = s[i];
}
s[n] = ' ';
}
#define benchmark(x) printf("