mercoledì 27 marzo 2019

Sostituzione di stringhe in C

Ho scritto in C una funzione simile alla String.Replace() di C#: purtroppo la libreria standard del linguaggio C non fornisce tale funzionalità che quindi va implementata in proprio.
Come prototipo ho deciso di utilizzare qualcosa del genere:

int StrReplace(char *str, int maxstr, char *pattern, char *replace);

dove str è la stringa da modificare, maxstr la dimensione allocata per tale stringa, pattern la stringa da ricercare e replace la stringa da utilizzare in sostituzione.
La String.Replace() di C# sostituisce tutte le occorrenze che trova mentre la mia funzione, per semplicità, si limita alla prima occorrenza. Per lavorare come la String.Replace() è possibile sfruttare il valore di ritorno che sarà 1 nel caso di sostituzione avvenuta o 0 se non c'è stata alcuna sostituzione (o <0 in caso di errore), permettendoci quindi di scrivere 

while( StrReplace(...) == 1 ) {};

Qui di seguito presento il codice completo e le spiegazioni.
La funzione svolge il compito di cercare una sottostringa (chiamata pattern) e sostituirla con una nuova (replace).
I possibili valori di ritorno sono: 
  • -1 se non c'è sufficiente spazio allocato per l'eventuale sostituzione;
  • 0 se la sottostringa pattern non è stata trovata;
  • 1 se la sottostringa pattern è stata trovata e sostituita da replace.
Ecco un esempio d'uso:

char miastringa[80];
strcpy(miastringa, "Quel ramo del lago di Como");
StrReplace(miastringa,80,"Como","Garda");
printf(miastringa);

Provare per credere! Ed ecco qui l'implementazione:

int StrReplace(char *str, int maxstr, char *pattern, char *replace)
{
    int i, j;

    int lenstr = (int)strlen(str);
    int lenpattern = (int)strlen(pattern);
    int lenreplace = (int)strlen(replace);
    int diff;

    if ((lenstr - lenpattern + lenreplace) >= maxstr)
        return -1;        // not enough space

    for (i = 0; i <= (lenstr - lenpattern); i++)
    {
        if (strncmp(&str[i], pattern, lenpattern) == 0)    // if found
        {
            if (lenpattern == lenreplace)
            {
                strncpy(&str[i], replace, lenreplace);
               
                return 1;
            }
            else if(lenpattern > lenreplace)
            {
                diff = lenpattern - lenreplace;
                strncpy(&str[i], replace, lenreplace);
               
                // compact
                j = i + lenreplace;
                for (;j<lenstr-diff;j++)
                    str[j] = str[j+diff];
                str[j] = '\0';
               
                return 1;
            }
            else // lensearch < lensubst
            {
                diff = lenreplace - lenpattern;

                // expand
                j = lenstr - lenpattern + lenreplace;
                str[j] = '\0';
                for(; j > i + lenpattern;j--)
                    str[j] = str[j - diff];

                strncpy(&str[i], replace, lenreplace);

                return 1;
            }
        }
    }

    return 0;
}


Alcune note sul funzionamento possono aiutare la comprensione.
In base alla lunghezza delle due stringhe, quella da cercare e quella da inserire, ci possono essere tre possibilità:
  • se hanno la stessa lunghezza per la sostituzione ci basta una sovrascrittura;
  • se la stringa da inserire è più lunga di quella esistente bisognerà copiare la nuova stringa e poi compattare la parte a destra;
  • se la stringa da inserire è più lunga di quella esistente bisogna creare lo spazio espandendo la parte destra, per poi inserire la nuova sottostringa.
Nell'ultimo caso, dovendo espandere la stringa, c'è il rischio di sforare la dimensione massima: per evitare questo è stato aggiunto all'inizio della funzione un controllo sulla lunghezza che si può ottenere con un'eventuale sostituzione.