Funktion zum Entfernen verbotener Zeichen

21635
Karl
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 = ' ';
        }
    }
}

Ich habe diese Funktion verwendet, um einen String zu entfernen, der eines der folgenden Zeichen enthält: \, /,:,?, ", <,>, |. Dies ist für den Namen einer Datei. Dieses Programm läuft einwandfrei. Es ändert einfach ein Zeichen von Die Zeichenfolge wird zu einem Leerzeichen, wenn das entsprechende Zeichen das verbotene Zeichen ist. Ich habe jedoch ein Gefühl gegen diese Verwendung der switch-Anweisung. Ich benutze einfach die Case-Syntax hier, aber das ärgert mich irgendwie. Ich mag es einfach nicht. Hat jemand anderes einen besseren Vorschlag für eine bessere Implementierung in diesem Fall erhalten?

Antworten
19
Wenn ein Zeichen nicht verboten ist, dann lassen wir es. Karl vor 9 Jahren 0
Nennen Sie es replaceForbiddenChars, da es mehrere Zeichen ersetzt, anstatt sie zu entfernen und zu bearbeiten. Fred Nurk vor 9 Jahren 1
@Fred: Wenn keiner der Fälle in einer switch-Anweisung übereinstimmt, wird der Kontrollfluss nach der switch-Anweisung fortgesetzt. Dieses Verhalten ist perfekt definiert. sepp2k vor 9 Jahren 0
@ sepp2k: Danke, es ist klar definiert. Ich bin mir nicht sicher, warum ich das dachte, aber ich mache es für Artikel, die ich über (Mikro-) Optimierung gelesen habe. Fred Nurk vor 9 Jahren 0
Anscheinend haben Sie das * -Symbol (Sternchen) vergessen. Es ist auch verboten. vor 9 Jahren 0

7 Antworten auf die Frage

19
doppelgreener

Deklarieren Sie eine Zeichenfolge, die die ungültigen Zeichen enthält: "\\/:?"<>|". Alles, was Sie tun müssen, ist zu prüfen, ob sich der Char im Array befindet. Verwenden Sie dazu eine native Funktion oder schreiben Sie eine Methode, CharInString(char* needle, string* haystack)die den Inhalt des bereitgestellten Heuhaufens durchläuft, um zu prüfen, ob sich die Nadel darin befindet.

Ihre Schleife sollte so aussehen:

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

Es ist wartungsfreundlicher und lesbarer. Sie können feststellen, ob Sie ein Zeichen ganz leicht dupliziert haben, und da Sie es mit jeder beliebigen Zielzeichenfolge und beliebiger Zeichenfolge illegaler Zeichen, die Sie gerade für sich selbst erstellt haben, eine generische RemoveIllegalChars(string* targetString, string* illegalChars)Methode erstellen können, die an einer beliebigen Stelle in Ihrem Programm verwendet werden kann.

Ich benutze diese Zeiger möglicherweise falsch. Mein C ++ fu ist schwach ... vorerst.

+1, ich habe auch empfohlen, eine Zeichenfolge zu verwenden, um die verbotenen Zeichen zu speichern. Ich möchte hinzufügen, dass es durch diese Änderung sehr einfach ist, die verbotenen Zeichen als Parameter zur Funktion "removeForbiddenChars" hinzuzufügen. Wenn also jemals ein Bedarf entstehen sollte, kann dies in Situationen verwendet werden, in denen unterschiedliche Zeichensätze verboten sind. Sie können auch die `find`-Methode verwenden, um herauszufinden, ob sich ein Zeichen in einer Zeichenfolge befindet, sodass Sie nicht unbedingt eine` CharInString'-Funktion schreiben müssen (oder Sie könnten einen einfachen Wrapper um `find` schreiben). sepp2k vor 9 Jahren 0
@ sepp2k: Wir scheinen hier auf der gleichen Wellenlänge zu sein! :) Ich werde meine Antwort mit der `find'-Methode aktualisieren. doppelgreener vor 9 Jahren 0
Vielleicht sind die Dateinamen kurz und wir rufen diese Funktion nicht oft auf. Beachten Sie jedoch, dass die vorgeschlagene Lösung O (n * m) für die Anzahl der Zeichen in der Zeichenfolge (n) und die Anzahl der unzulässigen Zeichen in der Zeichenfolge ist Zeichenfolge (m). WilliamKF vor 9 Jahren 0
16
messenger

Sie könnten immer transform verwenden

#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;
}
Ich habe das nicht einmal gesehen, als ich gerade meine Antwort veröffentlichte. Noch eine andere Möglichkeit, dies mit einem anderen STL-Algorithmus zu tun :) Mark Loeser vor 9 Jahren 0
Dasselbe gilt für Lambda: `std :: transform (str.begin (), str.end (), str.begin (), [& verboten] (char c) {return forbidden.find (c)! = Std :: string.) :: npos? '': c;} ` Jon Purdy vor 9 Jahren 2
5
sepp2k

Eine Sache, die ich an Ihrer Funktion ändern würde (zusätzlich zu der Empfehlung von Jonathan, zum Speichern der verbotenen Zeichen einen String zu verwenden), ist der Argumenttyp von removeForbiddenCharto string&anstelle von string*. Im Allgemeinen wird es in C ++ als empfehlenswert angesehen, wenn möglich Verweise über Zeigern zu verwenden (siehe zum Beispiel diesen Eintrag in der C ++ - faq-lite).

Eine weitere, geringfügige kosmetische Änderung, die ich empfehlen würde, ist das Umbenennen der Funktion in removeForbiddenChars(Plural), da dies mehr beschreibt.

Sie überprüfen niemals die Gültigkeit der Zeichenfolge * s. Wenn in der Funktion removeForbiddenChar ein nullptr übergeben wird, wird versucht, ein nullptr dereferenzieren. Dies impliziert, dass der Aufrufer von removeForbiddenChar vor dem Aufruf von removeForbiddenChar nach nullptr suchen sollte. Der Aufrufer muss dies jedoch nicht unbedingt beachten, es sei denn, er zeigt die internen Daten von removeForbiddenChar an. Wenn der Verweis anstelle eines Zeigers übergeben werden muss, bedeutet dies, dass Ihre Absicht lautet: "Sie MÜSSEN eine gültige Zeichenfolge haben, um removeForbiddenChar aufrufen zu können." YoungJohn vor 6 Jahren 0
5
Mark Loeser

Oder, hier ist noch eine andere Möglichkeit, indem Sie alle Elemente aus der STL verwenden:

#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
MSN

C verfügt über eine hilfreiche Funktion size_t strcspn(const char *string, const char *delimiters), die Sie zusätzlich zu implementieren können. Die ASCII-Version ist ziemlich schnell. Es verwendet einen Bitvektor, um die Trennzeichen zu testen.

Wenn Sie auf der Suche nach Leistung sind, ist dieses hier schwer zu schlagen. EvilTeach vor 9 Jahren 0
4
Martin York

Lösung ohne bedingte Verzweigung.
Platz für Zeitoptimierung.

Vereinfachter Algorithmus:

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];
    }
}

Oder die C ++ 0x-Version:

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

Benötigen Sie einfach die Daten:

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
                            "\000\001\002\003\004\005\006\007"
                            "\010\011\012\013\014\015\016\017"
                            "\020\021\022\023\024\025\026\027"
                            "\030\031\032\033\034\035\036\037"
                            "\040\041 \043\044\045\046\047" // replaced \042(") with space
                            "\050\051\052\053\054\055\056 " // replaced \057(/) with space
                            "\060\061\062\063\064\065\066\067"
                            "\070\071 \073 \075  " // replaced \072(:)\074(<)\076(>)\077(?) with space
                            "\100\101\102\103\104\105\106\107"
                            "\110\111\112\113\114\115\116\117"
                            "\120\121\122\123\124\125\126\127"
                            "\130\131\132\133 \135\136\137" // replaced \134(\)
                            "\140\141\142\143\144\145\146\147"
                            "\150\151\152\153\154\155\156\157"
                            "\160\161\162\163\164\165\166\167"
                            "\170\171\172\173\174\175\176\177"
                            "\200\201\202\203\204\205\206\207"
                            "\210\211\212\213\214\215\216\217"
                            "\220\221\222\223\224\225\226\227"
                            "\230\231\232\233\234\235\236\237"
                            "\240\241\242\243\244\245\246\247"
                            "\250\251\252\253\254\255\256\257"
                            "\260\261\262\263\264\265\266\267"
                            "\270\271\272\273\274\275\276\277"
                            "\300\301\302\303\304\305\306\307"
                            "\310\311\312\313\314\315\316\317"
                            "\320\321\322\323\324\325\326\327"
                            "\330\331\332\333\334\335\336\337"
                            "\340\341\342\343\344\345\346\347"
                            "\350\351\352\353\354\355\356\357"
                            "\360\361\362\363\364\365\366\367"
                            "\370\371\372\373\374\375\376\377";
1
jcoleman

Ähnlich wie strcspnis strpbrk, aber anstatt Offsets zurückzugeben, wird ein Zeiger auf die nächste Übereinstimmung und NULL zurückgegeben, wenn keine weiteren Übereinstimmungen vorhanden sind. Dies macht den Austausch so einfach wie:

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