void removeForbiddenChar(string* s)
{
    string::iterator it;

    for (it = s->begin() ; it < s->end() ; ++it){
        switch(*it){
        case '/':case '\':case ':':case '?':case '"':case '<':case '>':case '|':
            *it = ' ';
        }
    }
}


我使用此函数删除了具有以下任意字符的字符串:\,/,:,?,“,<,>,| |。用于文件名。该程序运行得很好,当相应的字符为禁止字符时,它只是将字符串的字符更改为空白。但是,我对switch语句的这种使用有一种感觉。我只是不喜欢,在这种情况下,还有其他人对更好的实现有更好的建议吗?

评论

如果不禁止使用字符,那么我们将其保留。

将其命名为replaceForbiddenChars,因为它代替而不是删除并处理多个字符。

@Fred:如果switch语句中的所有情况都不匹配,则控制流在switch语句之后继续。该行为已得到完美定义。

@ sepp2k:谢谢,它定义明确。我不确定为什么会这样,但我会将其归咎于我一直在阅读的有关(微)优化的文章。

似乎您忘记了*符号(星号)。也被禁止。

#1 楼

声明一个包含非法字符的字符串:"\/:?"<>|"。您所需要做的就是检查char是否在数组中,因此为此使用本机函数,或编写方法CharInString(char* needle, string* haystack)循环遍历提供的干草堆的内容以检查指针是否在其中。
您的循环应最终看起来像这样:

string illegalChars = "\/:?\"<>|"
for (it = s->begin() ; it < s->end() ; ++it){
    bool found = illegalChars.find(*it) != string::npos;
    if(found){
        *it = ' ';
    }
}


它更易于维护和可读。您可以说出您是否很容易地复制了一个字符,并且因为您可以使用自己为自己创建的通用RemoveIllegalChars(string* targetString, string* illegalChars)方法的任何目标字符串和任何非法字符串进行复制。

我可能使用了那些指针错误。我的C ++ fu很弱...暂时。

评论


\ $ \ begingroup \ $
+1,我也建议使用字符串存储禁止的字符。我要补充一点,此更改使将禁用字符作为参数添加到removeForbiddenChars函数非常容易,这样,如果有需要,可以在禁止使用不同字符集的情况下使用它。您也可以使用find方法来查找字符是否在字符串中,因此您不必编写CharInString函数(或者您可以编写一个简单的find封装器)。
\ $ \ endgroup \ $
–sepp2k
2011年1月27日在16:24



\ $ \ begingroup \ $
@ sepp2k:我们似乎在同一波长! :)我将使用find方法更新答案。
\ $ \ endgroup \ $
–doppelgreener
2011年1月27日在16:25

\ $ \ begingroup \ $
也许文件名很短,我们并没有太多调用此函数,但是请注意,建议的解决方案是字符串(n)中的字符数和字符数为O(n * m)字符串(m)中的非法字符。
\ $ \ endgroup \ $
– WilliamKF
2011-2-9在2:04

#2 楼

您可以随时使用transform

#include <algorithm>
#include <string>
#include <iostream>

const std::string forbiddenChars = "\/:?\"<>|";
static char ClearForbidden(char toCheck)
{
    if(forbiddenChars.find(toCheck) != string::npos)
    {
         return ' ';
    }

    return toCheck;
}

int main()
{
    std::string str = "EXAMPLE:";
    std::transform(str.begin(), str.end(), str.begin(), ClearForbidden);
    std::cout << str << std::endl;
    return 0;
}


评论


\ $ \ begingroup \ $
我刚刚发布答案时甚至都没有看到这个。使用不同的STL算法实现此效果的另一种方法:)
\ $ \ endgroup \ $
– Mark Loeser
2011-1-28的0:31

\ $ \ begingroup \ $
与lambda相同:std :: transform(str.begin(),str.end(),str.begin(),[&forbidden](char c){return forbidden.find(c)!= std :: string :: npos?'':c;}
\ $ \ endgroup \ $
–琼·普迪(Jon Purdy)
2011年1月28日在2:37



#3 楼

或者,这是通过使用STL中的所有内容来完成此操作的另一种方法:

#include <algorithm>
#include <string>
#include <iostream>

bool isForbidden( char c )
{
    static std::string forbiddenChars( "\/:?\"<>|" );

    return std::string::npos != forbiddenChars.find( c );
}

int main()
{
    std::string myString( "hell?o" );

    std::replace_if( myString.begin(), myString.end(), isForbidden, ' ' );

    std::cout << "Now: " << myString << std::endl;
}


#4 楼

关于您的函数,我将要改变的一件事(除了乔纳森(Jonathan)建议使用字符串来存储禁止的字符),是removeForbiddenCharstring&而不是string*的参数类型。在C ++中,通常认为在可能的情况下使用对指针的引用是一种很好的做法(例如,请参见C ++ faq-lite中的此项)。对removeForbiddenChars(复数)起作用,因为它更能说明其作用。

评论


\ $ \ begingroup \ $
您永远不会检查字符串* s的有效性,因此,如果在removeForbiddenChar函数中传递了nullptr,则会尝试取消引用nullptr。这意味着removeForbiddenChar的调用者应该在调用removeForbiddenChar之前检查nullptr,但是除非调用者查看了removeForbiddenChar的内部结构,否则调用者不一定会意识到这一点。要求传递引用而不是指针,这表明您的意图是:“您必须具有有效的字符串才能调用removeForbiddenChar。”
\ $ \ endgroup \ $
– YoungJohn
2013年12月12日23:06

#5 楼

C附带有用的功能size_t strcspn(const char *string, const char *delimiters),您可以在此之上实现该功能。 ASCII版本非常快。它使用位向量测试分隔符。

评论


\ $ \ begingroup \ $
如果您正在寻找性能,这是很难击败的。
\ $ \ endgroup \ $
– EvilTeach
2011-1-28 14:45

#6 楼

无条件分支的解决方案。
交换空间以优化时间。

简化算法:

void removeForbiddenChar(string* s)
{
    for (string::iterator it = s->begin() ; it < s->end() ; ++it)
    {
        // replace element with their counterpart in the map
        // This replaces forbidden characters with space.
        (*it) = charMap[*it];
    }
}


或C ++ 0x版本:

void removeForbiddenChar(std::string* s)
{
    std::transform(s->begin(), s->end(), [](char c) => {return charMap[c];});
}


只需要数据:

char    charMap[] =
                            // The majority of characters in this array
                            // map the poistion to the same character code.
                            //  charMap['A']  == 'A'
                            // For forbidden characters a space is in the position
                            //  charMap['<']  == ' '
                            //  Note: \xxx is an octal escape sequence
                            "q4312078q0q4312078q1q4312078q2q4312078q3q4312078q4q4312078q5q4312078q6q4312078q7"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01 34567" // replaced 2(") with space
                            "0123456 " // replaced 7(/) with space
                            "01234567"
                            "01 3 5  " // replaced 2(:)4(<)6(>)7(?) with space
                            "01234567"
                            "01234567"
                            "01234567"
                            "0123 567" // replaced 4(\)
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567"
                            "01234567";


#7 楼

strcspn相似的是strpbrk,但它不返回偏移量,而是返回指向下一个匹配项的指针,如果没有更多匹配项,则返回NULL。这使得更换非常简单:

while ((filename = strpbrk(filename , "\/:?\"<>|")) != NULL)
    *filename++ = '_';