Togliere doppi spazi da un char*

Sapete mica se in una qualche libreria standard di 'C' esiste una funzione che tolga eventuali doppi spazi da una stringa?
io ho guardato la string.h, ma mi sembra che no, non ci sia....

io ho buttato giù 'sta roba qui:

char * toglispazi(char stringa[])
{
    byte i = 0;
    byte j = 0;

    bool trovato = 0;

    // toglie ogni spazio doppio da una stringa
    while (stringa[i])
    {
        // fino alla fine della stringa
        if (stringa[i] == ' ')
        {
            // si spazio, il primo trovato
            if (!trovato)
            {
                trovato = 1;
                stringa[j++] = stringa[i++];
            }
            else
            {
                // lo scarto
                i++;
            }
        }
        else
        {
            // no spazio, va via dritto
            stringa[j++] = stringa[i++];
            trovato = 0;
        }
    }

    // finita stringa, la tappo con uno 0
    stringa[j] = 0;
    return stringa;
}

ditemi voi se vi sembra possa andare, a me sembra che possa

Secondo me, per semplificare il codice, potresti usare la memcpy quando trovi il doppio spazio.

pero', con memcpy dovrei copiare "un passo indietro" l'intera sottostringa ancora da esaminare
che se poi ne trovo un altro di spazio, devo ripetere la copia
o sbaglio?

No, non sbagli. Mi pare più efficiente il tuo algoritmo.

Un dubbio, l’ultima istruzione, stringa[j] = 0; ma se nella stringa non ci sono spazi oppure c’e’ ne solo 1, questa cosa fa?

Se non trova doppi spazi scrive sull'ultimo carattere dello array uno zero
Trattandosi di stringa lì uno zero è già scritto, poco cambia
Se invece trova un qualsiasi numero di doppi spazi la stringa sarà più corta, e quindi devo "chiuderla" artificialmente prima dalle sua fine naturale

Però, scusa, se non ci sono spazi singoli o doppi, a che serve:
stringa[j++] = stringa[i++]; // no spazio, va via dritto

non basta solo incrementare i e j di uno invece di fare una copia ?

Se non ci sono...
Ma siccome non lo so se ci sono...
Dovrei cercarli preventivamente, ciclando una volta di più lo array, non so se vale la pena
Forse, mettendo una variabile locale che ricorda se ne ha trovato almeno 1, potrei saltare questa copia inutile fino alla prima occorrenza, ma anche qui non so se ne vale la pena, dovrei aggiungere una variabile e testare per ogni carattere, per risparmiare i primi spostamenti a vuoto...

Bel algoritmo. Effettivamente riguardando il codice non mi pare si possa ottimizzare di più.

Grazie
Magari qualcuno più bravo di me nel codice oggetto può risolvere i miei dubbi
Però per quel che mi serve, per ora, questo i basta

Ciao, non so se può piacerti querst'implementazione, provala, io ho fatto un test veloce con gcc mi sembra che funzioni, rimuove N spazi quindi se due non vanno bene ma tre sono leciti allora l'implementazione non è aderente alle specifiche

char stringaIniziale[50] = "Ciao a tutti  quanti   i programmatori ";
eliminaSpaziDoppi(stringaIniziale, sizeof(stringaIniziale));

eliminaSpaziDoppi(char *sIngresso, byte nLen)
{
  char *sDestinazione = sIngresso;
  while(*sIngresso != 0 && nLen>0)
  {
     while(*sIngresso == ' ' && *(sIngresso+1) == ' ' && nLen>0)
     {
        sIngresso++;
        nLen--;
     }
     *sDestinazione++ = *sIngresso++;
     nLen--;
  }
  *sDestinazione = 0;
}

La variabile della dimensione gestisce stringhe di limitata dimensione, se serve gestire stringhe più lunghe basta modificare da byte a unsigend int il parametro
Ho un dubbio riguardo questa riga:

*sDestinazione++ = *sIngresso++;

che temo possa essere un undefined behaviour, se così fosse basta assegnare e poi incrementare separatamente i due puntatori.

Stasera sono a comperare casa a Varese
Temo che non potrò provare prima di domani sera
Comunque grazie...

Standardoil:
Se non ci sono...
Ma siccome non lo so se ci sono...
Dovrei cercarli preventivamente, ciclando una volta di più lo array, non so se vale la pena
Forse, mettendo una variabile locale che ricorda se ne ha trovato almeno 1, potrei saltare questa copia inutile fino alla prima occorrenza, ma anche qui non so se ne vale la pena, dovrei aggiungere una variabile e testare per ogni carattere, per risparmiare i primi spostamenti a vuoto...

Ci avevo pensato anche io, nell'else basterebbe vedere se i == j, in questo caso non hai ancora trovato spazi, però aggiungi un test in più ad ogni giro rallentando un pelo.
In altre parole, peggiori il caso in cui ci sono gli spazzi, perchè deve fare un test in più, per migliorare il caso in cui non ci sono, sempre che la copia di un singolo carattere sia significativamente più lenta del test.
Io eviterei di complicare il codice per una cosa di questo tipo, ma non so quanti cicli di clock servono per fare questa copia.
Vediamo se il mio socio, che sicuramante ci sta leggendo, ha qualcosa da aggiungere pure lui :wink:

Alla fine, il tuo algoritmo è estremamente lineare, mi sa che sarà dura fare di meglio.

Chiedo scusa se dico una sciocchezza ma per queste situazioni ho sempre usato replace().

String originale = "ciao a tutti i        programmatori";
String modificata = originale.replace("  ", "");

ed in teoria string mette lo "\0" finale in automatico se non ricordo male.

Sì, a cosa, ma ti obbliga ad usare oggetti Stringa, io vorrei rimanere nelle stringhe classiche di 'c'

Per fabpolli: non la ho potuta provare, come avevo anticipato, però gli ho dato uno sguardo, in pratica tu fai coi puntatori quello che io faccio con gli indici degli array, bello
Per il socio: bravo, non avevo pensato al test dei due indici invece di una variabile apposta...

Alla fine sono molto simili le due implementazoni, la mia è più parsimoniosa di memoria non allocando nessuna variabile aggiuntiva la tua rispetta regole più stringenti per quanto riguarda l'accesso agli array che di solito impone l'uso di indici e non di puntatori (MISRA) in termini di velocità d'esecuzione credo non vi siano differenze sostanziali, si potrebbe provare a misurarle, se ne hai voglia.

Ah, per la cronaca, rileggendo il codice ho visto che anche tu togli N spazi, quindi la premessa che avevo fatto non ha senso

fabpolli:
Ho un dubbio riguardo questa riga:

*sDestinazione++ = *sIngresso++;

che temo possa essere un undefined behaviour, se così fosse basta assegnare e poi incrementare separatamente i due puntatori.

Nessun undefined behaviour, tutto ok.

Io avrei fatto qualcosa come la prima implementazione di stdoil, inutile scervellarsi più di tanto, è un algoritmo che gira in O(n) e scendere sotto questo mi sembra difficile. Concentrerei gli sforzi di ottimizzazione altrove, se proprio servisse.

Grazie per la conferma!
Il codcie di Standardoil è sicuramente più chiaro in termini di comprensione del mio e come detto anche poco sopra non credo che in termini prestazionali vi siano differenze ho voluto provare a cambiare leggermente il punto d'osservazione giusto per il gusto di travare un'altra soluzione, un esercizio fine a se stesso insomma :slight_smile: che è la forma politicamente corretta di "mi son preso cinque minuti per scappare dalla noia del lav.... ehm" :slight_smile: