如何快速用另一个字符替换字符串中的字符我认为测试不需要通用方式

问题描述

我在技术测试中被问到这个问题。
他们询问如何将字符串中的 ' ' 更改为 '_'。
我认为他们不想要共同的答案。像这样(我可以保证)

void replaceChar(char originalStr[],size_t strLength,char originalChar,char newChar 
{
    for(size_t i = 0 ; i < strLength ; i++)
    {
        if(originalStr[i] == originalChar)
        {
            originalStr[i] = newChar ;
        }
    }
}

所以我是这样回答的。使用单词。 (其实我没有写代码,他们只是想解释怎么做)
我认为将字符串的每个 8 字节(64 位操作系统)与掩码 8 字节进行比较。
如果它们相等,则一次替换 8byte。

cpu 读取小于 WORD 的数据时,cpu 应该做清除剩余位的操作。
它很慢。所以我尝试使用 WORD 来比较字符。

void replaceChar(char originalStr[],char newChar // 
    {
        size_t mask = 0;
        size_t replaced = 0;
        for(size_t i = 0 ; i < sizeof(size_t) ; i++)
        {
            mask |= originalChar << i;
            replaced |= newChar << i;
        }
        
        for(size_t i = 0 ; i < strLength ; i++)
        {
            
            // if 8 byte data equal with 8 byte data filled with originalChar
            // replace 8 byte data with 8 byte data filled with newChar 
            if(i % sizeof(size_t) == 0 && 
               strLength  - i > sizeof(size_t) && 
               *(size_t*)(originalStr + i) == mask)
            {
                *(size_t*)(originalStr + i) = replaced;
                i += sizeof(size_t);
                continue;
            }

            if(originalStr[i] == originalChar)
            {
                originalStr[i] = newChar ;
            }
        }
    }

有没有更快的方法??

解决方法

在通往快速之路的第一件事就是正确。原始提案的问题在于 sizeof(s) 应该是 strlen(s) 的缓存值。那么明显的问题是,这种方法扫描字符串两次——首先找到终止字符,然后是要替换的字符。

这应该由已知长度的数据结构或数据结构来解决,有足够的保证多余数据,以便可以一次处理多个字节而不会出现未定义的行为。

一旦解决了这个问题(OP 已经被编辑以解决这个问题),建议的扫描 8 个字节的数据的所有字节相同的方法的问题是通用案例确实有 8 个连续的字符,但也许仅 7. 在所有这些情况下,都需要扫描同一区域两次(在扫描字符串终止字符的基础上)。

如果字符串长度未知,最好使用低级方法:

while (*ptr != 0) {
   if (*ptr == search_char) {
       *ptr = replace_char;
   }
   ++ptr;
}

如果字符串长度已知,最好使用库方法std::replace,或者低级对应

for (auto i = 0; i < size; ++i) {
    if (str[i] == search_char) {
        str[i] = replace_char;
    }
}

任何体面的编译器都能够自动向量化这一点,尽管编译器可能会生成比预期更多的内核(一个内核用于小尺寸,一个用于中间,另一个用于处理 32 或 64 字节的块)。