我将假设您的意图与“修剪”通常暗示的有点不同。“修剪”通常用于表示从字符串的开头和/或结尾删除额外的空格,但您似乎意味着输入中每个地方都有一系列空格,您希望输出中有一个空格。
我还假设您设置在处理 C 样式字符串的类 C 实现上。如果这不是给定的,那么仅使用迭代器和标准算法将变得更加简单和清晰。
假设是这种情况,我想我会做更多这样的事情:
bool copy_word(char *&dest, char const *&src) {
while (isspace(*(unsigned char *)src))
++src;
while (*src && *src != ' ') {
*dest = *src;
++dest;
++src;
}
return *src != '\0';
}
void trim_whitespace(char *dest, char const *src) {
while (copy_word(dest, src))
*dest++ = ' ';
*dest = '\0';
}
这里有两点需要牢记:首先,当您要执行一系列操作时(例如,跳过空白,然后复制非空白),将其编码为序列可能更简洁,而不是编码为通过单个循环的不同路线。其次,当您使用 时isspace
,您必须1将操作数强制转换为某种无符号类型以避免 UB。
编辑:为了它的价值,我整理了一个小测试/基准程序,以查看我的代码与 OP 答案中的代码相比如何。
#include <ctype.h>
#include <time.h>
#include <vector>
#include <set>
#include <deque>
#include <iostream>
#include <string>
#include <algorithm>
void iterative_trim_whitespace(const char* src, char* target){
bool firstspace(true);
while (*src != '\x0'){
if (firstspace){
*target++ = *src++;
}
else{
src++;
}
if (firstspace && isspace(*(src - 1))){
firstspace = false;
}
else{
firstspace = true;
}
}
*target = '\x0';
}
struct my_isspace {
bool operator()(char ch) {
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v';
}
};
bool copy_word(char *&dest, char const *&src) {
my_isspace check;
while (check((*src)))
++src;
while (*src && !check(*src))
*dest++ = *src++;
return *src != '\0';
}
void trim_whitespace(char *dest, char const *src) {
while (copy_word(dest, src))
*dest++ = ' ';
*dest = '\0';
}
void show(std::string const &label, double t) {
std::cout << "Time for " << label << " " << t << " seconds\n";
}
template <class test, class T>
double timer(test t, T a, T b) {
clock_t start = clock();
t(a, b);
clock_t stop = clock();
return double(stop-start)/CLOCKS_PER_SEC;
}
void generate_string(std::vector<char> &dest, size_t size) {
for (int i=0; i<size; i++) {
if (rand() % 5 == 0)
dest.push_back(' ');
else
dest.push_back(rand() % 26 + 'A');
}
dest.push_back('\0');
}
int main() {
static const int size = 1024 * 1024 * 100;
std::vector<char> src, dest;
generate_string(src, size-2);
dest.resize(size);
show("Original", timer(iterative_trim_whitespace, &src[0], &dest[0]));
show("Jerry's", timer(trim_whitespace, &dest[0], &src[0]));
return 0;
}
至少当我运行它时,我得到:
Time for Original 0.749 seconds
Time for Jerry's 0.468 seconds
我可能应该补充一下:正如我在评论中提到的那样,isspace
我使用的编译器的实现相当慢,至少与我在这里抛出的简单编译器相比。然而,公平地说,如果这样做的部分好处只是被简单地实现为函数对象,我不会感到惊讶(无论如何),这通常使编译器更容易为其生成内联代码。
对于它的价值,还有两点:
- 微软的链接时代码生成大大减慢了这两者
- 无论哪种方式,修剪都比最初生成输入要快得多
1嗯,从技术上讲,它可能是char
一个无符号类型开始 - 但它是不寻常的,你不应该指望它。您的所有输入也可能属于您char
可能持有的 ASCII 字符子集,在这种情况下,它似乎工作得很好——但这就是有害的:您可以测试它(尽可能多)但是,除非您使用包含编码为负数的字符的文本执行此操作char
,否则它看起来很好。然后,当您的法语/西班牙语/挪威语/等等,客户对其进行测试时,它会掉下来。