# String manipulieren



## nasir (26. Feb 2010)

Hallo Leute,

ich wollte fragen, wie ich einen String so manipulieren kann, dass er bei der suche direkt unten drunter ein bindestrich ("-") reinschreibt, für gefunden.

Also z.B.

Input:

```
Java kann sehr viel spaß machen!
```

und es soll "n seh" gesucht werden und dann soll so eine Ausgabe folgen:

Output:

```
Java kann sehr viel spaß machen!
        -----
```

Ich habe leider überhaupt keinen Ansatz. Ich weiß nämlich nichtmal von wo bis wo er in gefunden hat, um dann ein neuen String mit leerzeichen hinzuzufügen, dann von 8 - 13 den char '-' zu füllen und den rest wieder mit leerzeichen.

Bitte um hilfe

Gruß

nasir


----------



## @x.l (26. Feb 2010)

Du kannst in einen String nach eine bestimmten Zeichensequenz durchsuchen (bswp. String#indexOf) und du bekommst den Index, wo die gesuchte Sequenz beginnt.


----------



## Dow Jones (26. Feb 2010)

Die billigste Lösung wäre wohl mittels String.replaceAll(...), vielleicht reicht das in deiner Anwendung ja schon.


```
String satz = "Java kann sehr viel spaß machen!";
String muster = "n seh";

String markierung = satz
	.replaceAll(muster, "\7")
	.replaceAll("[^\7]", " ")
	.replaceAll("\7", muster.replaceAll(".", "-"));

System.out.println(satz);
System.out.println(markierung);
```
liefert wie gewünscht:

```
Java kann sehr viel spaß machen!
        -----
```

Ein paar Haken gibt's dabei natürlich:
- man muß natürlich darauf achten das das Zeichen 0x07 (die ASCII-Glocke) in den Texten nicht verwendet wird
- es werden auch alle Sonderzeichen wie z.B. "\n" durch Leerzeichen ersetzt (das könnte man noch umgehen indem man die zu schützenden Sonderzeichen in Zeile 6 in der eckigen Klammer mit angibt)
- wenn sich das Suchmuster über mehrere Zeilen erstreckt, also am Ende von Zeile x beginnt und am Anfang von Zeile x+1 aufhört, dann wird es nicht gefunden


----------



## DaveX (26. Feb 2010)

Hallo 

ich würde an deine Stelle Pattern Matching benutzen.
Erstell mal ne Class XXX , kopier die funktion rein und in der Main entsprechend aufrufen.
Bei Fragen bitte Posten. Ich hoffe das ist das was du suchst.

Greetz Dave


```
XXX.underLineIT("n seh", "Java kann sehr viel spaß machen!")

public static String underLineIT(String needle, String target){
		
		   Pattern pattern = Pattern.compile(needle);
		   Matcher matcher = pattern.matcher(target);
		   String result = "";
		   // Find all the matches.
		   while (matcher.find()) {
		   
			 for( int pos = result.length() ; pos < matcher.end() ; pos++ )
			 {
				 result += ( pos < matcher.start() ) ? " " : "-";
			 }		   
		   }
		   
		return target+"\n"+result;
	}
```


----------



## KalleM (26. Feb 2010)

Hallo,
hätte ein Frage zu Zeile 14:

result += ( pos < matcher.start() ) ? " " : "-";

was bedeutet oder wofür steht hier der Doppelpunkt?


----------



## eRaaaa (26. Feb 2010)

Galileo Computing :: Java ist auch eine Insel (8. Auflage) – 2.9.6 Der Bedingungsoperator
oder
http://www.java-forum.org/einfuehrungen-erste-schritte/10574-ternaerer-operator-x-y-0-0-100-a.html
= 


```
if(pos < matcher.start()){
	    result+= " ";
	}else{
	    result+= "-";
	}
```


----------



## nasir (26. Feb 2010)

vielen vielen Dank für eure Antworten. 
Bringt mich ein schritt weiter. Allerdings ist das Problem nicht erledigt.

Also einmal sollten es mehrere Suchkriterien geben.

z.B.


```
String sequenz = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        String[] unterstreichen = {"ABCDEF", "EFGHIJKLMNO", "0123"};
```

und am ende sollte dies dabei heraus kommen


```
ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
---------------           ----
```

Dachte daran deswegen einen indexOf zu nehmen um genau, den 2. String so zu manipulieren, dass an dieser Stelle die Striche angezeigt werden. Aber mir ist es nicht so ganz recht, weil das Problem geht noch weiter. Ich will diesen Manipulator nämlich auf eine GUI transferieren.
Also habe ich eine Schriftart auf ein TextPane genommen, wo alle Zeichen die selbe Breite haben.

z.B.


```
String sequenz = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        String[] unterstreichen = {"ABCDEF", "EFGHIJKLMNO", "0123"};
```

der EditorPane ist auf knapp 55 Zeichen lang, weil nach jedem 10. zeichen ein Leerzeichen und nach jedem 4. Leerzeichen ein \n folgt. Ist rein nur für die Übersichtlichkeit gedacht. So sieht der String aus:


```
ABCDEFGHIJ KLMNOPQRST UVWXYZ0123 456789ABCD EFGHIJKLMN
OPQRSTUVWX YZ01234567 89ABCDEFGH IJKLMNOPQR STUVWXYZ01
23456789
```

Und so sollte es danach aussehen:


```
ABCDEFGHIJ KLMNOPQRST UVWXYZ0123 456789ABCD EFGHIJKLMN
----------------            ----       ---------------
OPQRSTUVWX YZ01234567 89ABCDEFGH IJKLMNOPQR STUVWXYZ01
-            -----      ----------------            --
23456789
--
```

und was mir jetzt wirklich besonders schwer fällt ist z.B. dass mehrere Suchkriterien und zusammenverknüpft mit mehrzeiligen darzustellen. Ein Ansatz wäre da wirklich gut!

Gruß

nasir


----------



## DaveX (27. Feb 2010)

Ich hätte irgendwie nicht schlaffen können wenn ich's nicht gecodet hätte 
Ich weiss nicht ob es die speicherefiteintesste Lösung ist aber dein Beispiel funktioniert ;D

Aufrufen tut man das so:

```
ClassNameWhereItIsImplementedIn.underLineIT("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
"ABCDEF", "EFGHIJKLMNO", "0123"));
```

"String..." erwartet beliebig viele Argumente die mit KOMMA getrennt werden.
Ich weiss nicht mehr wie dieses feature heist Elipse glaube ich - verbessert micht wenns falsch ist.

Ich gehe erstmal jedes Suchwort durch und speichere in einer HashMap die Stellen aus dem target String die zu den gesuchten Wörtern gehören.

Danach gehe ich von 0 bis zu der Länge des target Strings und füge entweder Leerzeichen oder ein Unterstreichungssymbol zu dem result String hinzu falls die Stelle den Buchstaben des gesuchten Strings enthält.

Ich hoffe nicht allzu kompliziert erklärt.


```
public static String underLineIT(String target,String... needles )
{
		Map<Integer, Boolean> occurences = new HashMap<Integer, Boolean>();
		
		// Hier erstmal alle vorkommende Suchkriterien in einer Map abspeichern
		// key = position / value = zeichen des gesuchten Kriteriums
		for(String needle : needles)
		{
			Pattern pattern = Pattern.compile(needle);
			Matcher matcher = pattern.matcher(target);
			
			while ( matcher.find() )
			{
				for(int pos = matcher.start(); pos < matcher.end(); pos++){
					occurences.put(pos, true);
				}
			}
		}
		
		String result = "";

		for(int targetpos = 0; targetpos < target.length(); targetpos++ )
			result += (occurences.get(targetpos)!=null) ? "-" : " ";
		   
		return target+"\n"+result;
}
```


----------



## eRaaaa (27. Feb 2010)

DaveX hat gesagt.:


> Ich weiss nicht mehr wie dieses feature heist Elipse glaube ich



Die drei Punkte nennt man allgemein Ellipse (Ellipse (Sprache) ? Wikipedia)

In Java sind das dann Varargs


----------



## DaveX (27. Feb 2010)

Cool Danke  
da hat mir nur ein "l" gefehlt


----------



## nasir (28. Feb 2010)

Ich wollte erstmals danke sagen! Ich muss mir das ganze nochmal anschauen und verinnerlichen. 
So gut wie DaveX programmiert hat, bin ich lange noch nicht.

Wenn ich weitere Fragen habe, werde ich hier nochmals posten


----------



## DaveX (28. Feb 2010)

Danke für den Lob, ich muss sagen ich war auch mal ein Anfänger und vom Programmierguru bin ich auch noch Lichtjahre entfernt.
Denn es kommt immer was neues, und man lernt nie aus.

Ich kann auch jede Zeile von mir erklären, aber am besten ist es, wenn man es selbst untersucht und ausprobiert - denn dann lernt mal erst recht.
Auch wenn man schon was fertiges bekommt - stellt man sich fragen wieso ist das so gemacht worden und was die einzelnen Sachen machen.

@EDIT
mir ist grad eingefallen, dass ich statt Map<Integer, Boolean> occurences = new HashMap<Integer, Boolean>();
ein Set<Integer> benutzen sollte.

Eine Map ist Speicherverschwendung in diesem fall da ich die keys und values speichere
und ich will ja nur die positionen speichern.

So konnte ich statt zu sagen dass map an der stelle wo ein zeichen zu den gesuchten vorkommt true ist
einfach dem Set die position als integer hinzufügen - ein set hat keine doppelten einträge also kommen die gesuchten positionen nur einmal vor.

Dies konnte eine Aufgabe sein - mein Code zu verbessern mit Einsatz von Set statt Map ;D

Grüße
DaveX


----------



## Firestorm87 (1. Mrz 2010)

DaveX hat gesagt.:


> Eine Map ist Speicherverschwendung in diesem fall da ich die keys und values speichere
> und ich will ja nur die positionen speichern.


Hab mal spaßenhalber deine Map ersetzt (nicht durch ein Set, sondern direkt in den ErgebnisString) und durchwandere nicht mit jedem "needle" den String, sondern baue daraus zuerst ein Pattern, welches alle needles enthält und durchsuche dann nur 1x den String 

Wenn wir schon Speicherplatzoptimiert arbeiten, dann doch auch hier lieber mal ne Schleife einsparen 

Aber lesbarer wirds dadurch nicht 

```
public static String underLineIT(String target, String... needles) {
		StringBuilder pat = new StringBuilder();
		boolean firstRun = true;
		for (final String needle : needles) {
			pat.append(firstRun ? "[" : "|");
			firstRun = false;
			pat.append(needle);
		}
		pat.append("]");
		Pattern pattern = Pattern.compile(pat.toString());
		Matcher matcher = pattern.matcher(target);

		String result = new String(target);
		result = result.replaceAll(".", " ");
		char[] resultArray = result.toCharArray();
		while (matcher.find()) {
			for (int pos = matcher.start(); pos < matcher.end(); pos++) {
				resultArray[pos] = '-';
			}
		}

		return target + "\n" + String.valueOf(resultArray);
	}
```


----------



## nasir (1. Mrz 2010)

Weil ich doch so blöd war und euren Code nicht ganz verstanden habe (ich meine, muss ihn ja auch noch ändern usw. ) habe ich mal selbst einen Code gebastelt.

So siehts zurzeit aus:


```
public class underLine {

    static String needles(String s, String[] search)
    {
        StringBuffer ziel = new StringBuffer(extend(" ", s.length()));

        int start = 0, end = 0;

        for(int i = 0; i < search.length; i++)
        {
             for(int j = 0; j < s.length(); j = end )
             {
                 String needles = "";
                 start = s.toUpperCase().indexOf(search[i].toUpperCase(), end);
                 end = start + search[i].length();
                 if(0 <= start && end <= s.length())
                 {
                     for(int k = 0; k < search[i].length();k++)
                         needles += "-" ;
                     ziel.replace(start, end, needles);
                 }
                 else break;
             }
             end = 0;
        }

        return ziel.toString();
    }

    //Dies fuellt nur einen String mit leerzeichen aus, was genausolang ist, wie
    //der Quellstring String
    public static String extend(final String s, final int len)
    {
        final StringBuffer buffer = new StringBuffer(len);
        buffer.append(s);
        for(int t = s.length(); t < len; ++t)
            buffer.append(' ');

        return buffer.toString();
    }





    static String format(String s)
    {
        StringBuffer buffer = new StringBuffer(s);

        int abstand = 5;
        int zusatz_chars = (int)(s.length() / abstand);
        System.out.println("Es werden zusaetzliche Zeichen benoetigt: " +zusatz_chars);

        //Hier wird der String formatiert. Nach 5 Buchstaben folgt Leerzeichen.
        //Nach 4. Leerzeichen folgt neue zeile. Dementsprechend werden fuer
        //neue Zeile sowie leerstellen, eine zusaetzliche zahl gebraucht, damit
        //die Schleife auch bis zum ende geht.
        for( int i = 0, j = 0, k = 0; i < s.length() + zusatz_chars; i++)
        {
            if(j == abstand)
            {
                if (k == 4)
                {
                    buffer.insert(i,'\n');
                    k = 0;
                    j = 0;
                    continue;
                }
                else
                {
                    buffer.insert(i,' ');
                    k++;
                    j = 0;
                    continue;
                }
            }
            j++;
        }
        return buffer.toString();
    }


}
```



```
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here

        String s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        String[][] search = {   {"ABCD","3456789","90"},
                                {"234","ghi","klm"},
                                {"CDEFGh","opQRs","89"}
        };

        String[] result = new String[3];

        System.out.println("So siehts gewoehnlich aus!!");
        System.out.println("[" + s + "]");
        for(int i = 0; i < search.length; i++)
        {
            result[i] = underLine.needles(s,search[i]);
            System.out.println("[" + result[i] + "]");
        }  


        System.out.println("Hier finden wir genau diese Darstellung, die wir haben wollen!");
        System.out.println(underLine.format(s));

    }
}
```

Ausgabe: 



```
So siehts gewoehnlich aus!!
[ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]
[----                         -----------                         -------]
[      --- ---               ---           --- ---               ---     ]
[  ------      -----               --  ------      -----               --]
Hier finden wir genau diese Darstellung, die wir haben wollen!
Es werden zusaetzliche Zeichen benoetigt: 14
ABCDE FGHIJ KLMNO PQRST UVWXY
Z0123 45678 9ABCD EFGHI JKLMN
OPQRS TUVWX YZ012 34567 89
```

und ich bin immernoch am kniffeln, wie ich diese Darstellung herausbekomme. 


```
So siehts gewoehnlich aus!!
[ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]
[----                         -----------                         -------]
[      --- ---               ---           --- ---               ---     ]
[  ------      -----               --  ------      -----               --]
Hier finden wir genau diese Darstellung, die wir haben wollen!
Es werden zusaetzliche Zeichen benoetigt: 14
ABCDE FGHIJ KLMNO PQRST UVWXY
----
Z0123 45678 9ABCD EFGHI JKLMN
    -------------
OPQRS TUVWX YZ012 34567 89
                  --------
```

bzw. wenn ich alle mache dann


```
ABCDE FGHIJ KLMNO PQRST UVWXY
----
       ---  ---           
  -------       ------  
Z0123 45678 9ABCD EFGHI JKLMN
    -------------
   ----             ---  --- 
          ---  -------              
OPQRS TUVWX YZ012 34567 89
                  --------
                ----  
-----                   --
```


----------



## DaveX (1. Mrz 2010)

Hi Leider ist das nicht die Lösung, mit dem Aufteilen.
Ich wollte halt was sehr performantes bauen um die Lösung vom Firestorm87 zu toppen 

ich hab mal die Uterstreichungs Funktion neu gebaut 

```
public static String underLineITRecursiv(String target, String[] needles,StringBuilder sb)
	{
		int shorterThanTarget = 0;
		for (final String needle : needles)
		{
			if(target.startsWith(needle))
			{
				for (int i = 0; i < needle.length(); i++)
					sb.append("-");
				
				return underLineITRecursiv(target.substring(needle.length()),needles,sb);
			}
			
			if(needle.length()<= target.length())
				shorterThanTarget++;
		}
		if(shorterThanTarget == 0)
			return sb.toString();
			
		return underLineITRecursiv(target.substring(1),needles,sb.append(" "));
	}
```

in seiner habe ich nur den return wert reduziert dass nur die unterstreichung rauskommt
Hier der performance test:


```
public static void main(String[] args) {
	
        String target = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        String[] search1 = {"ABCD","3456789","90"};
        
        System.out.println(target);
        
        long s1 = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        System.out.println( JForum.underLineITRecursiv(target, search1, sb));
        long s2 = System.currentTimeMillis();
        System.out.println("Zeit: "+(s2-s1)+" milis");
       
        long s3 = System.currentTimeMillis();
        System.out.println(JForum.underLineIT2(target, search1));
        long s4 = System.currentTimeMillis();
        System.out.println("Zeit: "+(s4-s3)+" milis");
        
        
	}
```

ich habe den test noch mal extremisiert ;D

```
long s1 = System.currentTimeMillis();
        for(int x=0; x < 100000;x++)
        {
        	StringBuilder sb = new StringBuilder();
        	JForum.underLineITRecursiv(target, search1, sb);
        }
        long s2 = System.currentTimeMillis();
        System.out.println("Zeit: "+(s2-s1)+" milis");
       
        long s3 = System.currentTimeMillis();
        for(int x=0; x < 100000;x++)
        {
        	JForum.underLineIT2(target, search1);
        }
        long s4 = System.currentTimeMillis();
        System.out.println("Zeit: "+(s4-s3)+" milis");
```

Morgen versuche ich die Formatierungsgeschichte zu bauen wenn ich bisschen Zeit finde.


----------



## Firestorm87 (2. Mrz 2010)

DaveX hat gesagt.:


> Hi Leider ist das nicht die Lösung, mit dem Aufteilen.
> Ich wollte halt was sehr performantes bauen um die Lösung vom Firestorm87 zu toppen


Ziele braucht der Mensch :lol:


DaveX hat gesagt.:


> ich habe den test noch mal extremisiert ;D


Sagst du uns auch ohne dass wir das nun selber Copy&Pasten, was rausgekommen ist?
Nicht, dass Ich gewinnen will, aber die Größenordnung der Unterschiede würde mich schon interessieren....


----------

