# Aus einem String alle Leerzeichen entfernen



## neuesich (5. Mai 2012)

```
#include <stdio.h>

char *eraseAllBlanks(char *str)
{
    char *temp;

    while (*str!='\0'){
        while(*str=' ')
            str++;
        *temp=*str;
        temp++;
        str++;
    }
    return temp;
}

void main (void)
{
    char *in="Hallo Welt   ";
    printf("%s", eraseAllBlanks(in));
}
```
Das war meine Überlegung wie ich aus einem String alle Leerzeichen entfernen könnte, allerdings funktioniert es nicht. Ich habe auch keine Compilerwarnungen, weshalb ich dachte ich frag einfach mal nach. Wahrscheinlich habe ich das mit der Char-Zeiger Syntax noch nicht verstanden^^

Danke für jeden Hinweis!


----------



## ibafluss (5. Mai 2012)

Im main() in Zeile 19 erstellst du einen char-Zeiger, der auf einen konstanten String zeigt. Es wird also automatisch vom Compiler Speicher reserviert, in diesen Speicher wird "Hallo Welt      " reingeschrieben und dein char-Zeiger zeigt auf diese Konstante. Das ist aber wie schon erwähnt ein konstanter String -> du darfst ihn nicht ändern. Das machst du aber in der Funktion eraseAllBlanks(). 
Außerdem erstellst du in der Funktion ein char* temp. In Zeile 10 änderst du den Wert, auf den temp zeigt. Nur wohin zeigt temp? Irgendwohin -> Du änderst irgendeinen Speicher ab, ein weiterer Grund, warum das Programm abstürzt.

Du könntest die Sache mit einem Array in main() statt mit einem Zeiger lösen, oder du reservierst Speicher für den Zeiger mittels malloc() und beschreibst den reservierten Speicher mit "Hallo Welt     ".
Dasselbe gilt in der Funtion: temp() muss unbedingt zuerst auf freien Speicher zeigen, sonst darfst du da nichts ändern.

Lg


----------



## neuesich (5. Mai 2012)

Ok danke fürs bessere Verständnis.

Bei char-Arrays muss ich mich doch immer auf eine feste Größe festlegen, oder? Ich weiß leider  nicht wieviele Zeichen eingelesen werden, deshalb hab ich mich für char-Zeiger entschieden.

Aber da habe ich ja das Problem wenn ich mein "temp"-Element erzeuge weiß ich ja nicht wieviel Speicher ich allozieren muss, da ich nicht weiß wie groß der String nach Entfernung der Leerzeichen ist (okay ich könnte eine Funktion erstellen die die Leerzeichen zählt, aber das ist doch übertrieben oder?)


----------



## sheel (5. Mai 2012)

Ein char-Pointer und ein char-Array sind eigentlich ziemlich gleich...egal.
tmp der Funktion darfst du so auch nicht verwenden. Kein Speicher angelegt.

Du schreibst, du liest den String ein. Tastatur?

Jedenfalls musst du ihn für die Leerzeichenentfernung den Speicher nicht verkleinern.
Am Schluss sind dann eben ein paar Bytes unbenutzt. Stört doch nicht.


----------



## neuesich (5. Mai 2012)

Gut. Den String lese ich (später) aus einer Datei (ist aber erstmal nur ein konstanter Wert, um die Funktion umzusetzen)

Wie kann ich denn die Größe eines übergebenen String-Parameters ermitteln? Den muss ich sicherlich der Funktion mit übergeben, oder?
Wenn es keine andere saubere Möglichkeit gibt, dann nehm ich auch ein paar unbenutzte Byte in Kauf


----------



## sheel (5. Mai 2012)

Du brauchst die Größe in der Funktion nicht (aber du könntest sie mit strlen bekommen).
Mach die Funktion einfach so, dass der leerzeichenlose String da rein kommt,
wo der Ursprüungliche drin war.
Länger kann er ohne Leerzeichen nicht geworden sein, also muss er reinpassen.

Meiner Meinung nach wäre das auch das "sauberste", statt dafür mehrere mallocs zu bemühen.


----------



## neuesich (5. Mai 2012)

> Mach die Funktion einfach so, dass der leerzeichenlose String da rein kommt,
> wo der Ursprüungliche drin war.
> Länger kann er ohne Leerzeichen nicht geworden sein, also muss er reinpassen.
> 
> Meiner Meinung nach wäre das auch das "sauberste", statt dafür mehrere mallocs zu bemühen.


Hm wie ist das wenn ich das erzeugte Element mit dem überschüssigen Bytes erneut einem Element zuordnete (also ein zweites mal malloc verwende, um Speicherplatz zu reservieren)?
Werden da die unnötigen Bytes mitkopiert oder "passt es" dann? Wenn die überschüssigen Bytes nicht mitkopiert werden würden, dann würde ich am Ende der Funktion ein weiteres malloc benutzen...


```
#include <stdio.h>

char *eraseAllBlanks(char *str){
    char *runner=str;
    char *temp=(char *)malloc(sizeof(str));

    while (*runner!='\0'){
           while(*runner=' ')
               runner++;
           *temp=*runner;
           temp++;
           runner++;
        }
    return temp;
}

void main (void)
{
    char *in="Hallo Welt   ";
    printf("%s", eraseAllBlanks(in));
}
```


----------



## ComFreek (5. Mai 2012)

Da fehlt ein Gleichheitszeichen:

```
while(*runner=' ')

while(*runner==' ')
```


----------



## sheel (5. Mai 2012)

Und ein free fehlt auch, neuesich.


----------



## neuesich (5. Mai 2012)

```
#include <stdio.h>

char *eraseAllBlanks(char *str){
    char *runner=str;
    char *temp=(char *)malloc(sizeof(str));

    while (*runner!='\0'){
           while(*runner==' ')
               runner++;
           *temp=*runner;
           temp++;
           runner++;
        }
    free(runner);
    return temp;
}

void main (void)
{
    char *in="Ha llo W e lt   ";
    printf("%s", eraseAllBlanks(in));
}
```


----------



## ibafluss (5. Mai 2012)

free() bedeutet den Speicher wieder freigeben, den du mit malloc() alloziert hast. runner zeigt ja auf gar keinen reservierten Speicher, hier darfst du kein free() anwenden. temp zeigt auf reservierten Speicher, da gehört das free() hin.

Lg


----------



## neuesich (6. Mai 2012)

Ok, ja ich hab das etwas zu eilig gepostet^^

```
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

char *eraseAllBlanks(char *str){
    char *runner=str;
    char *temp=(char *)malloc(strlen(str));

    while (*runner!='\0'){
           while(*runner==' ')
               runner++;
           temp=runner;
           temp++;
           runner++;
    }
    runner=temp;
    free(temp);
    return runner;
}

void main (void){
    char *in="Ha llo W e lt   ";
    printf("%s", eraseAllBlanks(in));
}
```
Das Programm gibt nichts aus. Anscheinend läuft beim Ablaufen was schief (vermutlich irgendwelche Zuordnungen)


----------



## saftmeister (6. Mai 2012)

Also, lassen wir das mal rekapitulieren:

- In der Funktion eraseAllBlanks erstellst du einen Character-Pointer, der auf den Speicher wir str zeigt.
- Dann wird ein Character-Pointer temp erzeugt und speicher in Größe von Länge(str) angefordertert - hier wird schon mal nicht auf NULL geprüft, was fahrlässig ist, aber gut, es funktioniert zu mindest.
- Anschließend kommt die Schleife, die über alle Zeichen in runner (und damit auch in str) prüft.
- Innerhalb der Schleife wird noch mal eine Schleife geloopt, in der alle Leerzeichen ignoriert werden.
- Anschließend wird der Pointer von runner an den Pointer von temp geschrieben
- Jetzt wird der temp- und der runner-Pointer um eins erhöht.

Soweit ist das alles in Ordnung, aber jetzt kommt der Denkfehler:
- Nach der Schleife wird der aktuelle Pointer von runner mit dem Pointer von temp überschrieben.
- Jetzt wird temp aus dem Speicher entlassen, temp zeigt jetzt irgendwo hin nur nicht mehr an den Anfang, wo der original angeforderte Speicher ursprünglich mal adressiert wurde - hier hast zu zu mindest mal ein Memory-Leak
- Dann wird der aktuelle Zeiger von runner zurück gegeben - runner zeigt aber auch nicht mehr auf den Anfang, sondern sehr wahrscheinlich auf das Ende des Strings, eine \0.

Damit ist klar, warum nichts ausgegeben wird.


----------



## neuesich (6. Mai 2012)

Ok, ich habe dann gedacht man könnte einen Pointer auf die erste Position des Strings setzen, was aber eine if-Bedingung (zum Abfangen des ersten Buchstabens) vor der while-Schleife bedeuten würde:


```
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

char *eraseAllBlanks(char *str){
    char *runner=str,*start;
    char *temp=(char *)malloc(sizeof(str));

    if(*runner!='\0'){
           while(*runner==' ')
               runner++;
    temp=runner++;
    start=temp++;
    }

    while (*runner!='\0'){
           while(*runner==' ')
               runner++;
           temp=runner++;
           temp++;
    }

    free(temp);
    return start;
}

void main (void){
    char *in="Ha      llo   W e lt   ";
    printf("%s", eraseAllBlanks(in));
}
```
Funktioniert leider nicht, es wird der komplette String zurückgegeben.
Alternativ könnte ich auch einen Zähler einbauen, den ich dann zurücklaufe?


----------



## ibafluss (6. Mai 2012)

Probiers besser mal so:

Übergib als erstes Mal den String als const char* an die Funktion, so verhinderst du, dass du da was am konstanten String herumarbeitest.
Du erstellst in der Funktion einen Pointer, der auf auf freien Speicher in der Größe des übergebenen Strings zeigt und kopierst dann einfach alles, was kein Leerzeichen ist, in den freien Speicher. Du brauchst nur den übergebenen Pointer und noch einen für den neuen String. Den reservierten Speicher kannst du dann jedoch erst wieder im main() freigeben, in der Funktion darfst du das nicht.

2. Möglichkeit wäre, die Funktion einfach so zu schreiben, dass sie den String bekommt, und noch einen weiteren Pointer auf freien Speicher. Den freien Speicher beschreibst du dann in der Funktion. Dann bräuchtest du auch keinen String zurückgeben, der würde dann ja schon im übergebenen Pointer stehen.

Lg


----------



## saftmeister (6. Mai 2012)

Was spricht denn dagegen, den allokierten Speicher an der Stelle frei zu geben, wo die Funktion eraseAllBlanks aufgerufen wird?

Außerdem ist der Vorschlag von ibafluss gar nicht so schlecht.

Ich würde es allerdings so machen:


```
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
 
char *eraseAllBlanks(const char *str)
{
  char *buf = malloc(strlen(str));
  if( buf == NULL )
    return NULL;

  while(*str)
  {
    if( *str != ' ' )
     *buf++ = *str;
    str++;
  }

  return buf;
}

int main (int argc, char **argv)
{
  char *in="Ha      llo   W e lt   ";
  char *out = eraseAllBlanks(in);

  printf("%s", out);

  free(out);
}
```


----------



## neuesich (6. Mai 2012)

Naja ein free() außerhalb der Funktion wirkt insofern komisch, dass ich mit dem erzeugten String weiterarbeiten will und es vielleicht nicht ersichtlich ist, dass an anderer Stelle Speicherplatz für den String alloziert wurde.
Das Ganze würde nacher folgendermaßen aussehen
1. Lies Zeile aus Datei in String
2. Entferne alle Leerzeichen aus dem String
3. Werte String aus (eigentliche Operation)
4. Speicherplatz wieder freigeben

Deshalb würden zwei Übergabeparameter die Sache eher unübersichtlicher machen, da das Entfernen der Leerzeichen eine Nebenoperation ist.

@saftmeister Dein Quelltext funktioniert leider auch nicht. Es wird immer ausgegeben "ata/Local", also ein Teil eines Pfades. Was ich mich auch frage ist, ob die Bedingung der Schleife 
	
	
	



```
while(*str)
```
 wirklich beim Stringende greift?!


----------



## saftmeister (6. Mai 2012)

Sorry, hab nicht getestet. Ich hab die gleichen Fehler reingebaut, wie bei dir...

Hier die überarbeitete funktionierende Version:


```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
char *eraseAllBlanks(const char *str)
{
  char *dst, *src;
  char *buf = malloc(strlen(str));
  if( buf == NULL )
    return NULL;

  memset(buf, 0, strlen(str));
 
  src = str;
  dst = buf;

  while(*src)
  {
    if( *src != ' ' )
     *dst++ = *src;
    src++;
  }
 
  return buf;
}
 
int main (int argc, char **argv)
{
  char *in="Ha      llo   W e lt   ";
  char *out = eraseAllBlanks(in);
 
  printf("%s", out);
 
  free(out);

  getchar();
}
```

Ja, *str greift bis zum String-Ende - zu mindest wenn der String 0-Terminiert ist.

Wenn ein String 0-Terminiert ist, dann hat er am Ende eine 0 stehen. Daher ist die while-Bedingung korrekt.

EDIT: Zum free()-Thema. Es gibt ein paar Standard-Funktionen, die genauso arbeiten. str_dup() z.B. macht das genau so. Außerdem kann man eine Funktion auch kommentieren ;-)


----------



## Thomas Darimont (6. Mai 2012)

Hallo,

könntest du nicht auch die C-String Funktion strtok verwenden?

```
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
  char input[] = "Hal  lo We  lt   !";
  char chr[] = " ";

  printf("%s\n",input);

  char* token;
  token = (char*)strtok(input,chr);
  while(token != NULL){
     printf("%s",token);
     token = (char*)strtok(NULL,chr);
  }
  
  printf("\n");
  system("PAUSE");	
  return 0;
}
```

Ausgabe:

```
Hal  lo We  lt   !
HalloWelt!
```

Gruß Tom


----------



## ibafluss (6. Mai 2012)

Aufpassen beim Speicher reservieren, wenn der Ausgangsstring keine Leerzeichen enthält und somit gleich groß ist, wie der resultierende String.


```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* eraseAllBlanks (const char *str)
{
    char *ret = (char*) malloc (strlen (str) + 1);  // strlen() gibt die Länge ohne \0 zurück daher +1
    int i;
    int j = 0;

    if (ret == NULL)
    {
        return NULL;
    }

    for (i = 0; i < strlen (str); i++)
    {
        if (str[i] != ' ')
        {
            ret[j] = str[i];
            j++;
        }
    }

    ret[j] = '\0';

    return ret;
}

int main()
{
    const char *in = "  W o  rd ";
    char *str = eraseAllBlanks (in);

    printf ("%s", str);

    free (str);
}
```


----------



## saftmeister (6. Mai 2012)

Thomas Darimont hat gesagt.:


> könntest du nicht auch die C-String Funktion strtok verwenden?



Nicht, wenn der ursprüngliche String noch benötigt wird, denn strtok() verändert den Eingabestring.


----------



## Matthias Reitinger (6. Mai 2012)

saftmeister hat gesagt.:


> Nicht, wenn der ursprüngliche String noch benötigt wird, denn strtok() verändert den Eingabestring.


Was aber wohl kein Problem darstellt, zumindest nach dieser Beschreibung:



neuesich hat gesagt.:


> Das Ganze würde nacher folgendermaßen aussehen
> 1. Lies Zeile aus Datei in String
> 2. Entferne alle Leerzeichen aus dem String
> 3. Werte String aus (eigentliche Operation)
> 4. Speicherplatz wieder freigeben



Insofern könnte man die Funktion auch gleich in situ arbeiten lassen:


```
#include <stdio.h>

void eraseAllBlanks(char* str) {
  char* out = str;
  const char* in = str;

  while (*in) {
    if (*in != ' ') {
      *out = *in;
      ++out;
    }
    ++in;
  }

  *out = '\0';
}

int main() {
  char str[] = "Hal  lo We  lt   !";

  puts(str);
  eraseAllBlanks(str);
  puts(str);

  return 0;
}
```

Ausgabe:

```
Hal  lo We  lt   !
HalloWelt!
```

Grüße,
Matthias


----------

