Forum: Mikrocontroller und Digitale Elektronik Bestimmte Zeichen aus String entfernen? GCC


von Jan H. (janiiix3)


Lesenswert?

Nabend,

Wie oben erwähnt würde Ich gerne aus einem String bestimmte Zeichen raus 
löschen und die Zeichen die danach kommen, hinten an den String wieder 
anfügen, so daß dort keine Leerzeichen entstehen.

Test String: Gutxxen Axxxbend
Ziel String: Guten Abend

Was macht man jetzt am besten? Man könnte es ja einfach umkopieren mit 
ein paar abfragen in ein anderes Array..

Gibt es noch eine gute Alternative?

: Bearbeitet durch User
von Kolja L. (kolja82)


Lesenswert?

Unerwünschte Zeichen lösche ich meist mit der langen Taste üner der 
Return Taste auf der Tastatur.
Wenn du willst, dass dies eine Software macht, musst du schon sagen 
welche.

von Jan H. (janiiix3)


Lesenswert?

Ohja..

Es geht um Gcc.

von A. S. (Gast)


Lesenswert?

Schau dir einen Quellcode von strcpy an und kopiere nur wenn gewollt..

von Dr. Sommer (Gast)


Lesenswert?

1
#include <iostream>
2
#include <string>
3
#include <algorithm>
4
#include <iterator>
5
6
int main () {
7
  std::string str ("Gutxxen Axxxbend");
8
  std::cout << "Vorher: " << str << std::endl;
9
  str.erase (std::remove (begin (str), end (str), 'x'), end (str));
10
  std::cout << "Nachher: " << str << std::endl;
11
}
Ausgabe:
1
Vorher: Gutxxen Axxxbend
2
Nachher: Guten Abend

von Jan H. (janiiix3)


Lesenswert?

Dr. Sommer schrieb:
> #include <iostream>
> #include <string>
> #include <algorithm>
> #include <iterator>
>
> int main () {
>   std::string str ("Gutxxen Axxxbend");
>   std::cout << "Vorher: " << str << std::endl;
>   str.erase (std::remove (begin (str), end (str), 'x'), end (str));
>   std::cout << "Nachher: " << str << std::endl;
> }
> Ausgabe:Vorher: Gutxxen Axxxbend
> Nachher: Guten Abend

Das ist C++

von Dr. Sommer (Gast)


Lesenswert?

Jan H. schrieb:
> Das ist C++
Gut beobachtet... Und?

von Jan H. (janiiix3)


Lesenswert?

Gibt es diese Funktionen in reinem C überhaupt?

von Dr. Sommer (Gast)


Lesenswert?

Jan H. schrieb:
> Gibt es diese Funktionen in reinem C überhaupt?
Nö, aber wer redet von C?

von Axel S. (a-za-z0-9)


Lesenswert?

Du meine Güte, das ist doch nun wirklich keine Raketenwissenschaft. 
Offensichtlich mußt du Zeichen für Zeichen durch den String gehen und 
unerwünschte Zeichen überspringen. Du kannst noch überlegen, ob du das 
Ergebnis in einen neuen String kopierst oder gleich den alten String 
überschreibst.

von Jan H. (janiiix3)


Lesenswert?

Axel S. schrieb:
> Du meine Güte, das ist doch nun wirklich keine Raketenwissenschaft.
> Offensichtlich mußt du Zeichen für Zeichen durch den String gehen und
> unerwünschte Zeichen überspringen. Du kannst noch überlegen, ob du das
> Ergebnis in einen neuen String kopierst oder gleich den alten String
> überschreibst.

Ich will den alten String benutzen. Möchte ihn aber nicht überschreiben.

Die Zeichen sollen gelöscht werden die unerwünscht sind und jeweils soll 
kein Leerzeichen dazwischen stehen, also sollten die Zeichen die danach 
kommen nach links verschoben werden.

von Vril (Gast)


Lesenswert?

Jan H. schrieb:
> Dr. Sommer schrieb:
>> #include <iostream>
>> #include <string>
>> #include <algorithm>
>> #include <iterator>
>>
>> int main () {
>>   std::string str ("Gutxxen Axxxbend");
>>   std::cout << "Vorher: " << str << std::endl;
>>   str.erase (std::remove (begin (str), end (str), 'x'), end (str));
>>   std::cout << "Nachher: " << str << std::endl;
>> }
>> Ausgabe:Vorher: Gutxxen Axxxbend
>> Nachher: Guten Abend

OMG!

schon mal was von 'regular expressions' gehört?

von Dr. Sommer (Gast)


Lesenswert?

Nö, ist mir gänzlich neu, ich kenn nur kontextfreie Grammatiken.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <string.h>
4
5
// 1. Loeschen durch Kopieren:
6
void remove_1 (char * target, const char * str, int maxlen)
7
{
8
    while (*str && maxlen)
9
    {
10
        if (*str != 'x')
11
        {
12
            *target++ = *str;
13
            maxlen--;
14
        }
15
        str++;
16
    }
17
    *target = '\0';
18
}
19
20
// 2. Loeschen direkt im String:
21
void remove_2 (char * str)
22
{
23
    char * p;
24
25
    while (*str)
26
    {
27
        while (*str == 'x')
28
        {
29
            for (p = str; *p; p++)
30
            {
31
                *p = *(p + 1);
32
            }
33
        }
34
        str++;
35
    }
36
}
37
38
int main ()
39
{
40
    char s[64];
41
    char t[64];
42
43
    strcpy (s, "Gutenxxx Tagxxx");
44
    remove_1 (t, s, 63);
45
    printf ("%s -> %s\n", s, t);
46
47
    remove_2 (s);
48
    printf ("%s\n", s);
49
    return 0;
50
}
1
$ cc rem.c -o rem && ./rem
2
Gutenxxx Tagxxx -> Guten Tag
3
Guten Tag

von Jan H. (janiiix3)


Lesenswert?

Frank M. schrieb:
> void remove_2 (char * str)
> {
>     char * p;
>
>     while (*str)
>     {
>         while (*str == 'x')
>         {
>             for (p = str; *p; p++)
>             {
>                 *p = *(p + 1);
>             }
>         }
>         str++;
>     }
> }

Und wenn Ich jetzt..

Guxten Txag

Das die x entfernt werden und der Text danach jeweils um eins verschoben 
nach links rückt? Geht damit nicht?

von Dr. Sommer (Gast)


Lesenswert?

Drei Schleifen ineinander?! Das geht einfacher:
1
#include <stdio.h>
2
#include <stddef.h>
3
4
size_t remove_2 (char* str, size_t n) {
5
  size_t i, j;
6
  for (i = 0, j = 0; j < n; ++j)
7
    if (str[j] != 'x')
8
      str[i++] = str[j];
9
  return i;
10
}
11
12
int main () {
13
  char str [] = "Gutenxxx Tagxxx";
14
  printf ("Vorher : %s\n", str);
15
  str [remove_2 (str, sizeof (str))] = 0;
16
  printf ("Nachher: %s\n", str);
17
}

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Jan H. schrieb:
> Das die x entfernt werden und der Text danach jeweils um eins verschoben
> nach links rückt? Geht damit nicht?

Der Text wird doch nach links verschoben. Probiers einfach aus oder gehe 
das Programm mit Bleistift und Papier schrittweise durch.

Ein wenig den eigenen Grips anstrengen muss man beim Programmieren (oder 
beim Programm-Lesen) schon. Wenn Dich das jetzt überfordert, schnapp dir 
ein C-Buch und arbeite es systematisch durch. Pointer sind nicht das 
erste, was man bei C lernen sollte.

von Michael B. (laberkopp)


Lesenswert?

Dr. Sommer schrieb:
> Drei Schleifen ineinander?! Das geht einfacher:

Das ist doch dasselbe wie remove_1, nicht bemerkt ?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Dr. Sommer schrieb:
> Das geht einfacher:

Einfacher gehts immer. Ich wollte eine möglichst auch für den Anfänger 
gut lesbare und nachvollziehbare Möglichkeit aufzeigen. ;-)

Da der TO aber zerstöungsfrei kopieren möchte, ist remove_1() sowieso 
die für ihn geeignetere Lösung.

: Bearbeitet durch Moderator
von Dr. Sommer (Gast)


Lesenswert?

Michael B. schrieb:
> Das ist doch dasselbe wie remove_1, nicht bemerkt ?
Hmm stimmt, remove_2 ist wohl generell überflüssig, man kann remove_1 
auch zweimal den gleichen Pointer übergeben sodass es in-place 
arbeitet...

von Emma W. (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Das geht einfacher:

Jan H. schrieb:
> Ich will den alten String benutzen. Möchte ihn aber nicht überschreiben.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Dr. Sommer schrieb:
> Hmm stimmt, remove_2 ist wohl generell überflüssig,

Ja, remove_2() sollte lediglich eine Alternative aufzeigen, die der TO 
aber so gar nicht braucht, da er den Original-String nicht zerstören 
möchte.

: Bearbeitet durch Moderator
von Axel S. (a-za-z0-9)


Lesenswert?

Jan H. schrieb:
> Ich will den alten String benutzen. Möchte ihn aber nicht überschreiben.
>
> Die Zeichen sollen gelöscht werden die unerwünscht sind

Das widerspricht sich. Wenn du Zeichen im String löschst, dann 
überschreibst du ihn doch?

@Frank M. & Co.

Helfersyndrom? Das ist ein wirklich triviales Problem. Wenn er das nicht 
selber hinbekommt, dann bekommt er nie irgendwas hin.

von Emma W. (Gast)


Lesenswert?

Axel S. schrieb:
> Das widerspricht sich. Wenn du Zeichen im String löschst, dann
> überschreibst du ihn doch?

Einen String einlesen und einen anderen (geänderten) zurückgeben?

von Nils P. (torus)


Lesenswert?

Man muss das auch in C nicht zu Fuß machen:
1
#include <string.h>
2
#include <stdio.h>
3
4
void removeChars (char * input, const char *unwanted, char * output)
5
{
6
  char * substring = strtok(input, unwanted);
7
8
  *output = 0;
9
10
  while(substring != NULL) {
11
    strcat (output, substring);
12
    substring = strtok(NULL, unwanted);
13
  }
14
}
15
16
17
int main ()
18
{
19
  char output[100];
20
  char input[100];
21
22
  strcpy (input, "Gutxxen Axxxbend"); 
23
24
  removeChars (input, "x", output);
25
  puts (output);
26
  
27
  return 0;
28
}

Die Funktion erledigt auch gleich das entfernen mehrerer Zeichen. Kannst 
also xy und z in einem Rutsch aus dem String tilgen.

von Jan H. (janiiix3)


Lesenswert?

Nils P. schrieb:
> void removeChars (char * input, const char *unwanted, char * output)
> {
>   char * substring = strtok(input, unwanted);
>
>   *output = 0;
>
>   while(substring != NULL) {
>     strcat (output, substring);
>     substring = strtok(NULL, unwanted);
>   }
> }

Die Funktion ist auch gut. Funktioniert einwandfrei.
Habe mich wahrscheinlich ein wenig undeutlich ausgedrückt.

Den String, den Ich absuchen möchte, soll bearbetiet werden. Das heißt 
die Zeichen die dort in dem String vorkommen, die gesucht werden sollen 
daraus entfernt werden.

Die Funktion von Frank M. erfüllt genau diesen Zweck. Ich werde mir 
diese jetzt mal genauer anschauen, damit Ich es verstehe. Vielen Dank 
für die Hilfe.

von Jobst Q. (joquis)


Lesenswert?

Jan H. schrieb:
> Ich will den alten String benutzen. Möchte ihn aber nicht überschreiben.

Ein String braucht einen Puffer, also ein Stück Speicher, und einen 
Pointer.

Wenn du den alten String erhalten willst, musst du ihn in einen neuen 
Puffer kopieren. Wenn du den alten Puffer für den neuen String benutzen 
willst, geht das nicht ohne ihn zu überschreiben.


Hier eine Funktion zum kopieren ohne die Zeichen im String without:
1
char * stpcpywithout(char *t,const char *s,const char * without){
2
3
while (*s){
4
  if (strchr(without,*s)==NULL)*t++=*s;
5
  s++;
6
  }
7
*t=0;
8
return t;
9
}

Zurückgegeben wird ein Zeiger auf die Null am Stringende, er kann 
benutzt werden für das Anhängen von weiteren Strings oder Zeichen.

Du kannst die Kopierfunktion auch mit demselben Puffer verwenden, da der 
Lesezeiger s entweder identisch mit dem Schreibzeiger t ist oder ihm 
vorauseilt:
1
stpcpywithout( str, str,"x");

von Sebastian S. (amateur)


Lesenswert?

Das ist nur auf den ersten Blick eine einfache Sache.

Wird aus: "Gutxxen Axxxbend"
"xx" und "xxx" entfernt, so soll trotzdem NICHT aus:
"Text" -> "Tet" werden.

Auch ist "Axbend" nicht besonders schön, auch wenn vorher "Axxxxbend"
da stand.

Zugegeben, "xx" ist in der deutschen Sprache wohl nicht allzu häufig, 
aber einfach über die Anzahl an Zeichen zu filtern, wird auch in die 
Hose gehen.

Ob das überhaupt - ohne Wörterbuch - machbar ist, weiß ich nicht. Aber 
allzu viel Info hat der TO ja auch nicht rübergebracht.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Sebastian S. schrieb:
> Wird aus: "Gutxxen Axxxbend"
> "xx" und "xxx" entfernt, so soll trotzdem NICHT aus:
> "Text" -> "Tet" werden.

Ich nehme doch stark an, dass der TO das 'x' nur als ein exemplarisches 
Beispiel genommen hat, welches mit der tatsächlichen Aufgabe in der 
Realität gar nichts zu tun hat. Von daher sind Deine Folgerungen reine 
Spekulation.

von Schwarzseher (Gast)


Lesenswert?

Nils P. schrieb:
> void removeChars (char * input, const char *unwanted, char * output)
> {
>   char * substring = strtok(input, unwanted);
>
>   *output = 0;
>
>   while(substring != NULL) {
>     strcat (output, substring);
>     substring = strtok(NULL, unwanted);
>   }
> }

Diese Funktion ist ohne Not O(n²) statt O(n).

Ist bei kurzen Texten sicher egal, und mag elegant zu tippen sein, aber 
grade bei so Anfänger-Fragen sollte man dazuschreiben, dass diese 
Funktion um Welten schlechter ist als die anderen Vorschläge.

Sonst passiert sowas:

Jan H. schrieb:
> Die Funktion ist auch gut.

Nein. Sie ist eine ganz Kategorie schlechter als die anderen.

von Sebastian S. (amateur)


Lesenswert?

@Frank
>Ich nehme doch stark an, dass der TO das 'x' nur als ein exemplarisches
>Beispiel genommen hat, welches mit der tatsächlichen Aufgabe in der
>Realität gar nichts zu tun hat. Von daher sind Deine Folgerungen reine
>Spekulation.

Dann erst recht!
So kommen die Buchstaben "s" oder "f" bis zu 3-mal in einem Wort vor, 
ohne das der Herr Duden darüber meckert.

Aber worauf ich hinaus will ist: Es ist, ohne den gesamten Text im Auge 
zu behalten gefährlich, irgend welche Buchstabenkombinationen - ohne 
Quittung - zu ersetzen.

: Bearbeitet durch User
von Jan H. (janiiix3)


Lesenswert?

Leider kann Ich hier nicht so viele "Zitatzeilen" einfügen.

Ich spreche das Beispiel von Frank M. (remove_2) an...

Jetzt muss Ich doch eine Frage los werden.. Was genau macht der Zeiger 
"*p" wenn er nicht zurück gegeben wird? Und wieso wird "*str" richtig 
zurückgegeben wenn dieser gar nicht bearbeitet wird?!

von Axel S. (a-za-z0-9)


Lesenswert?

Wenn alle dürfen, dann ich auch. Die Funktion kopiert den String auf 
sich selbst und überspringt dabei alle Vorkommen des unerwünschten 
Zeichens. Da der Zielstring nicht länger sein kann als das Original, 
geht das immer.

Ich habe versucht, es sowohl kompakt als auch lesbar zu formulieren.
1
void remove_inplace(char *str, char unwanted)
2
{
3
  char *dst= str;
4
  char c;
5
  while ((c= *str++))
6
    if (c != unwanted)
7
      *dst++= c;
8
9
  *dst= 0;
10
}

so könnte man das verwenden:
1
int main(void)
2
{
3
  char s[]= "Halxxlo Wexlt!";
4
  printf("vorher:  '%s'\n", s);
5
  remove_inplace(s, 'x');
6
  printf("nachher: '%s'\n", s);
7
}

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Jan H. schrieb:
> Jetzt muss Ich doch eine Frage los werden.. Was genau macht der Zeiger
> "*p" wenn er nicht zurück gegeben wird? Und wieso wird "*str" richtig
> zurückgegeben wenn dieser gar nicht bearbeitet wird?!

Du meinst:
1
            for (p = str; *p; p++)
2
            {
3
                *p = *(p + 1);
4
            }

Der Pointer p läuft von der Stelle los, an der str gerade steht und 
"zieht" die Zeichen, die rechts davon stehen, um eine Stelle nach links. 
Die for-Schleife wird beendet, wenn der String zu Ende ist.

Beispiel: "abcxdef" im RAM untereinander geschrieben:
1
   a
2
   b
3
   c
4
   x  <--- da steht im Moment str und damit auch p wgen for (p = str;...)
5
   d
6
   e
7
   f
8
   \0 Hier ist der String zu Ende

Wegen *p = *(p + 1) wird nun das d an die Stelle von x kopiert, e an die 
alte Stelle von d, f an die alte Stelle von e. Als letztes wird noch der 
String-Terminator \0 nach links kopiert. Nach Durchlauf der for-Schleife 
steht dann im RAM:
1
   a
2
   b
3
   c
4
   d  <--- da steht immer noch str
5
   e
6
   f
7
   \0 Nun ist der String-Terminator eine Stelle weiter links
8
   \0 Der alte String-Terminator, hier steht jetzt p

Damit wurde das x eliminiert und der Teilstring "def" inkl. Terminator 
ist um eine Stelle nach links gewandert.

Deine Frage zeigt, dass Du noch waschechter Anfänger bist. Ich gebe Dir 
nochmal den Tipp, ein C-Buch systematisch durchzuarbeiten. Man kann 
keine Programmiersprache in einem Forum lernen. Außerdem sind Pointer 
bei einem C-Kurs erst dran, wenn man die Basis bereits gelernt hat.

: Bearbeitet durch Moderator
von A. S. (Gast)


Lesenswert?

Nur mal die Auflösung aus 
Beitrag "Re: Bestimmte Zeichen aus String entfernen? GCC":
1
char * strcpy(char *s1, const char *s2)
2
{
3
void *s = s1;
4
5
  while(*s2) *s1++ = *s2++;
6
  *s1 = (char)0;
7
  return s;
8
}
Anpassungen:
1) es braucht keinen Rückgabewert
2) es braucht nur einen Pointer (s1 = s2)
3) es braucht ein zu entfernendes Zeichen (int c)
4) umdrehen auf do while, damit keine Sonderbehandlung für abschließende 
0
5) Sicherstellen, dass c==0 nichts schlimmes macht (was immer auch dann 
passieren sollte)
1
void strcpy_masked(char *s, int c)
2
{
3
const char *s_read = s;
4
5
    do if(*s_read!=c || c==0) *s++ = *s_read; while(*s_read++);
6
}
und mit ein wenig mehr klammern und ein flg, das nicht unnötig kopiert 
wird (wobei diese Version meist nicht!! schneller ist):
1
void strcpy_masked(char *s, int c)
2
{
3
const char *s_read = s;
4
int copy=0;
5
6
    do
7
    {
8
        if(*s_read==c && c!=0) 
9
        {
10
            copy = 1;
11
        }
12
        else if(copy)
13
        {
14
            *s++ = *s_read; 
15
        }
16
        else
17
        {
18
             s++;
19
        }
20
    }while(*s_read++);
21
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.