in AnsiString nach \n\r suchen um Zeilenanzahl zu ermitteln



  • Hallo,

    folgendes Problem, ich bin dabei für meine Anwendung noch die Zwischenablage klar zu machen. Im gesamten Projekt wird es so gehandhabt, das wenn was kopiert wird, auch die Spaltenüberschriften mit in die Zwischenablage kopiert werden und es kann vorkommen, das mehrere Zeilen kopiert wurden, die dann auch wieder korrekt eingefügt werden sollen.
    Nun meine Frage wenn ich mir den gesamten Inhalt des Clipboard auf eine AnsiString kopiere und drin nach dem "\n\r" suche bekomm ich ja die Zeilenanzahl raus, aber wie mach ich das am effektivsten?

    Danke schon mal für die Antworten



  • Am effektivsten gehts mit ner Schleife und nem Pointer. Man kann das natürlich auch mit AnsiString Funktionen machen ... 😉



  • Hi,

    und wie müßte das mit Schleife und Pointer aussehen?
    Ich habs im Moment so:

    for(int i = 0; i<= help.Length();i++)
    {
      if(help.Pos("\r\n") != 0)  //help beinhaltet den Clipboardinhalt
      {
        anzZeilen++;
        help.Delete(1,(help.Pos("\r\n")+1));
      }
    }
    

    das funktoniert auch, aber vielleicht geht es noch irgendwie besser ?!



  • Wenn Du es sau schnell haben willst stelle folgenden Punkt
    Projekt -> Optionen -> Erweiterte Compiler-Optionen -> Registervariablen
    auf
    Schlüsselwort register
    und mach es ungefähr so:

    void __fastcall TForm1::Button2Click(TObject *Sender)
    {
    	int linecount;
    	AnsiString str;
    
    	str="Zeile1\n\rZeile2\n\rZeile3";
    	linecount=CountLines(str.c_str());
    	ShowMessage(linecount);
    }
    //---------------------------------------------------------------------------
    int TForm1::CountLines(char *str)
    {
    	register char *p;
    	register int lines=0;
    	register short comp=*((short*)"\n\r");
    
    	for(p=str; *p!=0; p++)
    	{	if(*((short*)p)==comp)
    			lines++;
    	}		
    	return lines;
    }
    

    Okay okay, Pointer in AnsiStrings ist manch einem ein Dorn im Auge, aber wir proggen schließlich in C/C++, greifen hier ja nur lesend zu und außerdem, schneller geht das wohl kaum noch 😉



  • Peter schrieb:

    Okay okay, Pointer in AnsiStrings ist manch einem ein Dorn im Auge, aber wir proggen schließlich in C/C++, greifen hier ja nur lesend zu [...]

    Macht unter Umständen absolut keinen Unterschied, da der Inhalt genau gleich zerstört werden könnte. Dazu muss man allerdings entweder Multi-Threaded Programmieren oder mitten drin Application->ProcessMessages() aufrufen, was einem Pseudo-Multithreading ähnlich kommt.

    -junix



  • Hallo junix ***freuhüpfundheftigwink*** 🙂



  • Könnt ihr bitte das Problem auch für mich(jemanden der noch nich soviel mit BCB gecodet hat) verständlich erklären?
    Danke



  • Grüss dir, Peter. Hast aj eh nur auf meinen Kommentar gewartet (o; - Ich warte immernoch bis dein Name auftaucht bei den Forentreffenanmeldungen (o;

    @ela: Das ist ein generelles problem mit Zeigern auf einen klasseninternen Puffer und hat nix mit dem BCB zu tun:

    Stell dir vor du schreibst einen String in die AnsiString Klasse. Diese reserviert einen char-puffer im Speicher und speichert den String ab. Nun holst du dir den zeiger auf diesen Puffer mittels c_str().
    Während du nun in diesem Puffer wuselst, schreibt aber ein anderer nen neuen String in dieses AnsiString Objekt oder mutiert den String.
    Nun kann es passieren, dass das AnsiString Objekt den kompletten Puffer freigibt und irgendwo nen neuen Puffer reserviert, während du mit dem alten Zeiger weiter wuselst.

    Der Punkt ist nun der, dass du in einem ungültigen Speicherbereich rumliest (und ev. auch schreibst). -> Segmentation faults oder mind. falsche Resultate sind vorprogrammiert.

    Dies passiert allerdings nur bei (quasi) Nebenläufigkeiten wie sie in MultiThreaded-Applikationen oder eben mit TApplication::ProcessMessages() auftreten können.
    Man spricht in diesem Zusammenhang auch oft von "nicht Threadsicher".

    In deinem Speziellen Fall allerdings, wo es sich beim AnsiString um ein lokales Objekt handelt, ist es absolut ungefährlich. Denn in diesem lokalen Objekt kann ja niemand ausser deine eigene Funktion rum pfuschen.

    ... ich glaub ich schreib das mal etwas ausführlicher in die FAQ...

    Ich hoffe ich habe mich verständlich ausgedrückt, ansonsten: Nachfragen.

    -junix

    [edit="junix"]Vielleicht noch ergänzend: Der Punkt, wieso ich immer wieder mit dem Ganzen hinterm Berg hervorkomme ist der, dass diese Problematik hässliche, kaum reproduzierbare Fehler verursachen kann. Schlussendlich ist es natürlich Ermessenssache, ob man sowas wies Peter vorgeschlagen hat verwenden will oder nicht. (Wie es bei vielen anderen gepredigten Regeln wie z.B. globalen Variablen, goto, etc. auch der Fall ist)

    Ich behaupte allerdings ganz kühn (und Peter wird mir da vermutlich zustimmen), dass die Minderheit hier im Forum wirklich die Tragweite der Verletzungen solcher Regeln sieht. Daher heisst hier auch immer die Devise, dass die Ratschläge und Tips sich auf der sicheren Seite bewegen sollten. Ansonsten ist niemandem geholfen.
    Da ich die wenigsten hier "kenne und einschätzen" kann, bete ich immer wieder diese Regeln und Probleme runter.[/edit]



  • Danke junix, ich hab verstanden was du meinst.

    @Peter
    der Code funzt sehr gut und da ich nicht nur nach dem "\r\n" suchen muss, sondern dann auch noch nach der Anzahl der Tabs die bis zu einem bestimmten Eintrag stehen werd ich mir gleich Deinen Code etwas abändern.

    Danke nochmal.

    Ich hab das gerade versucht dann noch so umzubauen, das die "\t" die vorhanden sind gezählt werden, aber die for-schleife nur solang laufen soll bis in dem String ein bestimmtes Wort steht z.B. Name, aber das geht noch irgendwie schief.

    Könnt ihr mir da bitte noch einen Tip geben?



  • Hi, ich nochmal,

    ich hab folgendes Problem:
    den Code vom Peter hab ich bei mir eingefügt, allerdings den Zweiten Til nicht in eine Funktion ausgelagert.
    Ich will nun die gleiche Funktion ein paar Zeilen später nochmal verwenden:

    ...
    comp  =*((short*)"\t");
    for(p=Zeile[0].c_str(); *p!=0; p++) 
        {    if(*((short*)p)==comp)  
                anzTab++; 
        }
    

    aber da funktioniert es nicht, WARUM 😕
    Ich hab es schon versucht indem ich mir alle Variablen nochmal angelegt habe (natürlich andere Namen) aber auch da funt es nicht

    Und noch etwas, wie bekomme ich es am besten hin das er für die Stelle

    *p!=0
    

    das aufgehört wird, wenn die Position von p auf den Eintrag "Namen" zeigt ?

    Also mal zum Beispiel:
    in Zeile[0] steht: "Test\tTyp\tName\tOperand\r\n"
    als Ergebnis muüsste ich jetzt für anzTab 2 rausbekommen.
    Aber das haut nicht hin.

    Ich bitte um HILFE 🙄



  • Wenn Du nur einen Character suchst (char) und nicht Zwei (short int), brauchst Du natürlich nicht mit (short*) zu casten. Alles weitere später, hab grad keine Zeit ...



  • ela schrieb:

    den Code vom Peter hab ich bei mir eingefügt, allerdings den Zweiten Til nicht in eine Funktion ausgelagert.

    Wieso nicht? Ist doch optimal für eine Funktion....

    Ansonsten äh, ja, _WAS_ funktioniert denn nicht? Was passiert beim durchsteppen?

    Was deine zweite Frage anbelangt.. das wirst du doch jetzt hoffentlich selbst hinkriegen oder? strcmp kennst du?

    -junix



  • @ junix:

    ich habs nicht ausgelagert, weil ich es erstmal testen will und schauen muss, wie ich das gesamte am besten hinbekomme, lauft es und ich kann die Funktion nutzen und vielleicht noch etwas abändern, dann wirds ausgelagert.

    Okay, also strcmp kenn ich. Da kann man zwei Strings miteinander vergleichen, du meinst also, ich solle test mit "Name" vergleichen? Und dann? In Test steht immer mehr drin wie nur "Name" ??? Ich weiß nicht auf was du hinaus willst, ich glaub ich hab nen Brett vorm Kopf.



  • ela schrieb:

    ich habs nicht ausgelagert, weil ich es erstmal testen will und schauen muss, wie ich das gesamte am besten hinbekomme, lauft es und ich kann die Funktion nutzen und vielleicht noch etwas abändern, dann wirds ausgelagert.

    Was spricht dagegen, einfach die Funktion zu verwenden? Wieso erst den Code in das Listing eingliedern und dann wieder in eine Funktion ausgliedern? Peters Code ist eigentlich eine typische - eigenständige Funktion, wieso nicht einfach so belassen? Ich rieche Spaghetti-Code...

    ela schrieb:

    Okay, also strcmp kenn ich. Da kann man zwei Strings miteinander vergleichen, du meinst also, ich solle test mit "Name" vergleichen? Und dann? In Test steht immer mehr drin wie nur "Name" ??? Ich weiß nicht auf was du hinaus willst, ich glaub ich hab nen Brett vorm Kopf.

    Du weisst doch, wie lang der String ist den du suchst? Also einen Teilstring extrahieren, vergleichen, gut is... Ausser Peter hat mal wieder ne schnellere Lösung...

    -junix



  • Hab zwischendurch mal schnell was zusammengeschustert:

    void __fastcall TForm1::Button2Click(TObject *Sender)
    {
    	int linecount;
    	AnsiString str;
    
    	str="Zeile1\tZeile2\tEla\nZeile3\tZeile4\t";
    
    	linecount=CountTabs(str.c_str(), "Ela");
    
    	ShowMessage(linecount);
    }
    //---------------------------------------------------------------------------
    int TForm1::CountTabs(char *str, char *termstr)
    {
    	register int lents=strlen(termstr);
    	register char *p;
    	register int tabs=0;
    
    	for(p=str; *p!=0; p++)
    	{   if(!strncmp(p, termstr, lents))
    			break;
    		if(*p=='\t')
    			tabs++;
    	}
    	return tabs;
    }
    

    @junix
    Über das Thema AnsiString und Pointer hatten wir uns ja auch schon mal früher unterhalten, gell 🙂
    In Punkto Forentreff, wenn es in der Stuttgarter Gegend stattfinden sollte und Du auch dabei bist wäre die Möglichkeit groß, daß ich da auch hinkomme. AndreasW habe ich inzwischen schon kennengelernt, allerdings nicht auf einem Forentreff sondern der Roadshow zum BuilderX 😉



  • Stimmt... strncmp hiess das Ding... ich wusste doch da gabs was (o:

    @Peter: Ne das ist auf Höhe Frankfurt (o: Aber ich fahr eh in Stuttgart vorbei, da könnte ich dich mitnehmen (o;

    -junix



  • Auch auf die Gefahr hin, mich hier sehr beliebt zu machen, will ich Euch
    meinen Vorschlag nicht vorenthalten.
    Als Freund der TStringList würde ich das ganze so lösen:

    TStringList* slTemp = new TStringList();
      slTemp->Text = help;
      int anzZeilen = slTemp->Count;
    

    Ob das allerdings auch funktioniert, wenn nur "\n" anstatt "\r\n" verwendet
    wird, weiß ich nicht. Leerzeilen werden natürlich auch mitgezählt.

    Nur mal so als Anregung.

    Gruß,

    Alexander



  • Abgesehen vom Problem, dass ja offensichtlich \t und nciht \r\n der Delimiter ist, hat die Idee natürlich auch was für sich... man könnte auch TStringList::CommaText verwenden (o: Aber wenn du nur Zeilen zählen willst, dann finde ich die StringList etwas äh Overkill...

    -junix



  • junix schrieb:

    Abgesehen vom Problem, dass ja offensichtlich \t und nciht \r\n der Delimiter ist

    Wieso offensichtlich? Wenn ich mir mal die Überschrift dieses Threads anschaue
    (und dabei den Dreher "\n\r" ignoriere) und einen Ausschnitt aus einem Posting
    von ela:

    ela schrieb:

    Ich habs im Moment so:

    ...
      if(help.Pos("\r\n") != 0)  //help beinhaltet den Clipboardinhalt
    

    Da könnte bei mir schon der Eindruck entstehen, als ob "\r\n" gesucht werden
    soll.

    junix schrieb:

    Aber wenn du nur Zeilen zählen willst, dann finde ich die StringList etwas äh Overkill...

    Deshalb ja auch meine kleines "Vorwort". Dennoch spricht schon was dafür, vor-
    gefertigte und (offensichtlich) bewährte Funktionen zu verwenden. Sicherlich
    gibt's da effizienteres als die Verwendung einer TStringList. Der Dreizeiler
    ist wesentlich überschaubarer und damit weniger fehleranfällig als eine selbst-
    geschriebene Routine mit for-Schleife (auch wenn die nicht wirklich kompliziert
    ist). Und falls man vielleicht die einzelnen Zeilen noch irgendwie weiterver-
    arbeiten will, bietet sich eine Stringliste vielleicht ohnehin an?

    Gruß,

    Alexander



  • OK, offensichtlich war vielleicht der falsche Ausdruck (o: Im Laufe des Threads stellte sich dies aber heraus.

    Was das Weiterverarbeiten betrifft: Sicherlich hast du recht. War an dieser Stelle allerdings nicht gefragt. Naja egal. Beide Lösungen haben ihre Vor- und Nachteile. Der grösste Nachteil an der StringList ist das leider etwas langsame ObjectPascal das hinter den Algorithmen steht.

    -junix


Anmelden zum Antworten