# [c++] 1. Zeichen in char* löschen



## Aloisia (10. April 2013)

Hallo, mein Problem ist das folgende: Ich bekomme einen char* number (Länge unterschiedlich) und will alle "führenden 0en" weghaben - Aus "00ef98vgF3" soll "ef98vgF3" werden (aber "0" bleibt "0", "00" wird "0")
 Mein Ansatz ist: 
l = Berechne die Länge des Strings
wenn l= 1, dann fertig
wenn nicht, dann prüfe ob 1. Zeichen 0 ist
wenn nein, fertig
wenn doch, dann "streiche es"; das heißt erschaffe einen buffer und schreib das 2te Element in das 1te des Buffers usw.
verwende nun die Werte des Buffers und lösche den Buffer
Starte den Test mit der neuen Zeichenkette erneut.

Was mache ich da falsch?


```
int l=strlen(number);

      while(l>1){
      if (number[0]=='0'){
         
      char* buf=new char[l-1];
         for (int i=0;i<l;i++){
         buf[i]=number[i+1];
         }
         
         number=buf;

         delete[] buf;
         l=strlen(number);
      }
      else{
      break;
      }
```


----------



## sheel (10. April 2013)

Hi und Willkommen bei tutorials.de,

andere Lösung:
Zähl zuerst die '0'´s, zB. in die Variable i.
Die ganze Bufferallozierung und Einzelumschichtung der chars dann ist
a) nicht pro 0 zu machen, sondern einfach ggf. mehrere Stellen auf einmal verschieben
b) Unnötig, weil das viel kürzer geht


```
//Zählen
int i = 0;
while(number[i] == '0')
    i++;

//dann:
memmove(number, &(number[i]), 1 + strlen(&(number[i])) );
```


----------



## Aloisia (10. April 2013)

sheel hat gesagt.:


> ```
> memmove(number, &(number[i]), 1 + strlen(&(number[i])) );
> ```


 Folgender Fehler an dieser Stelle:
0xC0000005: Zugriffsverletzung beim Schreiben an Position 0x002BDC88


----------



## Caligulaminus (10. April 2013)

Kann es sein, daß dein number const ist (ein Stringliteral womöglich)?


----------



## sheel (10. April 2013)

Falls nein, mach vor der ganzen 0-Entferne-Aktion mal das:

```
printf("Laenge: %d\n", strlen(number));
puts(number);
getchar();
```
Ausgabe davon?
(Das Programm wartet dann auf einen Enter-druck, bevor es zur Problemstelle weitergeht.)

Und zeig, um sicher zu gehen, doch mal die Codestelle her, wo number
angelegt/alloziert/übergeben wird.


----------



## Cromon (10. April 2013)

Hallo Aloisia,

Du hast als Tag in deinem Titel "C++" angegeben, ich sehe da jetzt den Zusammenhang zu deinem Code, da hast du effektiv C verwendet. Variante mit C++

```
std::string removeLeadingZero(const std::string& number) {
	std::string::const_iterator itr = std::find_if(number.begin(), number.end(), 
		[](const char& c) {
			return c != '0';
		}
	);

	if(itr != number.end() && itr != number.begin()) {
		std::string tmp;
		std::copy(itr, number.end(), std::back_inserter(tmp));
		return tmp;
	} else if(itr == number.end() && number.size() > 1) {
		return "0";
	}

	return number;
}

...
	std::cout << removeLeadingZero("00ef4300aa") << std::endl;
	std::cout << removeLeadingZero("00") << std::endl;
	std::cout << removeLeadingZero("0") << std::endl;
```

Ausgabe:
ef4300aa
0
0

Grüsse
Cromon


----------



## Aloisia (10. April 2013)

Ich muss das als Übung programmieren: Eine Klasse die mit sehr großen natürlichen Zahlen rechnen kann... z.B. 
	
	
	



```
Nat a= "123456789098765432";
Nat b= "3456545678765";
Nat c = a*b;
```
 als natürliche Zahl berechnen...
Der Aufruf 
	
	
	



```
Nat a = "1234567890987653";
```
 muss funktionieren -> ich habe einen Konstruktor in meiner Klasse deklariert: 
	
	
	



```
Nat(char* number="0");
```
 (Standard ist laut Aufgabe 0)
Diesen Konstruktor habe ich dann außerhalb der Klassendeklaration definiert.

```
Nat::Nat(char* number){
      number = sectZero(number);
...
}
```
 Da keine führende 0 vorkommen soll dieser Schritt. 
Weiterer Teil der Aufgabe ist dann die (verbleibende) char* Zeichenkette zu zerschneiden (je 2 Ziffern) und in ein Array einpflegen und dann damit rechnen.

Die Funktion 
	
	
	



```
char * sectZero(char* number);
```
 ist ebenfalls in der Klasse deklariert, außerhalb habe ich sie jetzt mit 

```
char* Nat::sectZero(char* number){


int i = 0;
while(number[i] == '0')
    i++;
 
//dann:
memmove(number, &(number[i]), 1 + strlen(&(number[i])) );
      return number;
   }
```
 definiert.

Ich habe in meiner Recherche NATÜRLICH schon die verschiedensten Lösungen mit der String-Klasse gesehen, da wir diese in der Vorlesung noch nicht hatten will ich die eigentlich unbedingt vermeiden...Die Vorlesung führt von C(******!) ++ also fast nur C zum Objektorientierten C++ Programmieren. Generell ist das OO für mich nicht das große Problem, doch in Java und VB.net habe ich nur mit Strings gearbeitet, und die sind meiner Meinung nach viel handlicher als diese char*!


----------



## Endurion (11. April 2013)

Cromon hat gesagt.:


> ```
> std::string removeLeadingZero(const std::string& number) {
> std::string::const_iterator itr = std::find_if(number.begin(), number.end(),
> [](const char& c) {
> ...



Urgs, sei mir nicht böse, aber das ist extrem unlesbar. Und es ist C++11, das hat man auch evtl. nicht zur Verfügung.


```
std::string removeLeadingZero( const std::string& number ) 
{
  size_t  firstNotZeroPos = number.find_first_not_of( "0" );
  if ( firstNotZeroPos == std::string::npos )
  {
    // nur Nullen oder leer
    return "";
  }
  return number.substr( firstNotZeroPos );
}
```


----------



## Aloisia (11. April 2013)

Ich werde heute mal abklären ob Strings erlaubt sind.


----------



## sheel (11. April 2013)

Trotzdem noch kurz zu den char*:

zB. das:

```
Nat a = "1234567890987653";
```
und dein gezeigter Standardkonstruktor sind fixe Stringliterale...

Gib der Klasse ihren eigenen Speicher und kopier so übergebene Werte da rein,
bevor du sie änderst. Dann gehts.


----------



## Cromon (11. April 2013)

Endurion hat gesagt.:


> Urgs, sei mir nicht böse, aber das ist extrem unlesbar.



Hallo Endurion

Selbstverständlich bin ich dir nicht böse wegen so etwas, jeder hat ein Recht auf seine Meinung, aber ich denke es ist dir auch klar, dass ich diese Aussage so nicht stehen lassen kann, da sie schlicht falsch ist. Ich bin einverstanden, dass deine Variante marginal lesbarer ist (hauptsächlich weil die Stringklasse unverständlicherweise keine substr-Methode anbietet die mit Iteratoren arbeitet), allerdings kann hier weder von "unlesbar" noch "extrem" die Rede sein. Ob ich jetzt mit number.end() prüfe oder std::string::npos sollte nicht dazu führen dass allen Lesern nur ein "Holy crap"-mässiges Fragezeichen im Kopf stehen bleibt. Auf weitere Aspekte wie Erweiterbarkeit (wie viele Collections haben find_first_not_of als Member und bei wie vielen Collections funktioniert std::find?) möchte ich hier nicht eingehen, das ist ja so gesehen nicht das Thema und es ist am Ende nichts weiter als eine subjektive Meinung.



Endurion hat gesagt.:


> Und es ist C++11, das hat man auch evtl. nicht zur Verfügung.



Dies kannst du so nicht sagen. Visual Studio unterstützt den C++11 Standard noch lange nicht, dennoch kann ich schon seit ca. 3 Jahren Lambdaausdrücke verwenden. Das selbe gilt auch für GCC, da sind Lambdaausdrücke auch schon ziemlich genau 3 Jahre offizielle supported. Man kann also getrost Lambdaausdrücke verwenden (ausser man verwendet clang, die sind was C++-Features anbelangt immer etwas hinterher, aber wer tut das schon für C++?) ohne sich irgendwelche Gedanken machen zu müssen. Im Gegensatz zu Betriebssystemen werden Compiler je länger je mehr ziemlich schnell auf den neusten Stand gebracht.

Grüsse
Cromon


----------



## Aloisia (11. April 2013)

Vielen Dank für eure Lösungen! Danke dass ihr euch Zeit genommen habt mir zu helfen.
Ich will aber lieber nichts verwenden, dass ich selber nicht zu 100% verstehe und reproduzieren kann.
Darum habe ich versucht eure Ansätze in etwas umzuschreiben, was sozusagen von mir kommt. Außerdem hat mir die Diskussion folgende meiner Denkfehler aufgezeigt: 
Wenn Parameter übergeben werden, kann man diese unter Umständen nicht überschreiben (fixe Strinliterale),
man kann Zeichenketten nicht einfach mit '=' in eine andere kopieren.

Hier meine Lösung:

```
//check for Leading zero, and cut eventually
      int i=0;
      while ((number[i]=='0')&&((strlen(number)-i)>1)){
         i++;
      }
      int l= strlen(number)-i;
      
      char* numwozero = new char[l];
      for(int j=0;j<l;j++){
         numwozero[j]=number[j+i];
      }
...
delete[] numwozero;
```

Ich lasse das Thema noch kurz offen, und werde es dann als beantwortet markieren! Vielen Dank nochmals!


----------



## sheel (11. April 2013)

Das schaut gut aus.

Nur etwas noch:
Beim j<l der for-Schleife eher j<(l+1) nehmen,
damit das Stringende-Zeichen am Schluss auch mitkopiert wird.

(Dass bei lauter Nullen eine am Schluss übrig bleibt, statt einem komplett leeren String,
ist wohl beabsichtigt(?) Hab bei meinem Code oben nicht so weit gedacht )


----------



## Aloisia (11. April 2013)

Gute Tipp, bei einem Test mit cout kam nach der letzten Stelle immer noch Müll.
Vorweg: so siehts jetzt aus


```
for(int j=0;j=l;j++){
         numwozero[j]=number[j+i];
      }
```

Ich dachte aber dass

```
char* numwozero = new char[l];
```
folgende Gestalt hat:

numwozero[#]01...l-1lElement1.Element2.Element...l-tes Element\0

Also, dass im l-ten Element sowieso der "Schlosstoken" drinnen steht, und ich das somit nicht überschreiben muss.


----------



## sheel (11. April 2013)

Genau :kopfaufdentisch: beim new muss auch +1 hin

Wenn man als Beispiel hallo nimmt (5 Buchstaben)
0 h, 1 a, 2 l, 3 l, 4 o, 5 Endzeichen
sind 6 Stück. Bei new gehört die Anzahl rein, nicht der max.Index, also 6

Und strlen liefert auch eine Anzahl, aber ohne Endzeichen.
hallo hat 5 Buchstaben, also 5.

Mal angenommen, es sind keine 0´s zu entfernen,
also streln minus 0 ist die volle strlen in Variable l.

Dann geht die Schleife solange i *<* l ist
0 1 2 3 4
h a l l o
Und das Endzeichen?


PS: Das j=l i letzen beitrag sollte doch j<=l heißen, oder?


----------



## Aloisia (11. April 2013)

j ist jetzt *<=* l

und prompt bekomme ich eine Fehlermeldung:

edit: Sorry, vergessen zu schreiben: der Fehler kommt im Schritt


```
delete[] numwozero;
```


----------

