# String teilen in gleich große strings



## turmaline (5. Feb 2012)

Hallo Leute,

kennt Ihr eine schlaue Bibliothek für Strings, die mir ermöglichen würde, ein string in n-große Strings zu teilen und dabei den String früher zu brechen, wenn da ein Leerzeichne vorgekommen ist.

So sieht es bei mir aus, aber vielleicht gibt es da Fehler die ich nicht sehe, bzw. ich das Rad neu erfunden habe... Oder vielleicht kann man das einfacher/perfomanter machen? Habt Ihr Ideen?


```
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;


public class StringUtils 
{
	private final static Logger LOGGER = Logger.getLogger(StringUtils.class .getName());

	public static List<String> getSubStrings(String text, int n) 
	{
		List<String> list = new ArrayList<String>();
		String s = new String(text);
		while(s.length() != 0)
		{
			s = s.trim();
			int end = n < s.length() ? n : s.length();
			String sub = s.substring(0, end);
			int nextIndex = end;
			int lastIndexOfSpace = sub.lastIndexOf(" "); // index of space character
			if(lastIndexOfSpace > 0)
			{
				end = lastIndexOfSpace;
				sub = s.substring(0, end);
				nextIndex = end + 1;
			}
			
			list.add(sub);
			s = s.substring(nextIndex);
		}
		LOGGER.info(text);
		LOGGER.info(list.toString());
		return list;
	}

}
```

und die Testklasse dazu:


```
import static org.junit.Assert.*;

import java.util.List;

import org.junit.Test;


public class SimpleTestCase 
{

	@Test
	public void test() 
	{
		String text = "lalalla lalall ll lalal lallalhzh";
		int n = 5;
		List<String> list = StringUtils.getSubStrings(text, n);
		assertEquals(7, list.size());
	}

}
```

Her mit Ideen 

Danke und Gruß,

madlena


----------



## Spacerat (5. Feb 2012)

Als erstes [c]new StringBuilder(completeString);[/c] und dann darauf die ganzen [c]substring()[/c] Methoden aufrufen. Vorher evtl. noch alle Leerzeichen daraus entfernen.


----------



## Paddelpirat (5. Feb 2012)

```
int lastIndexOfSpace = sub.lastIndexOf(" "); // index of space character
            if(lastIndexOfSpace > 0)
            {
                end = lastIndexOfSpace;
                sub = s.substring(0, end);
                nextIndex = end + 1;
            }
```

Der Teil ist bei dir wohl recht nutzlos, da du vorher sagst: 
	
	
	
	





```
s=s.trim()
```


----------



## xehpuk (6. Feb 2012)

Wieso ist der Teil nutzlos?

Ich stand auch mal vor der Problemstellung und habe mir etwas selbst geschrieben.


```
public final class Util {
	public final static String NEW_LINE = System.getProperty("line.separator");
	public final static char SPACE = ' ';
	public final static char ELLIPSIS = '\u2026';

	private Util() {}

	/**
	 * Gibt einen Text umbrochen aus.
	 * Beruecksichtigt wird, dass Woerter moeglichst nicht umbrochen werden.
	 * Als Trennzeichen wird nur das normale Leerzeichen (<tt>\u0020</tt>) erkannt.
	 * Passt der Text nicht in die angegebene Zeilenanzahl, so wird dies durch Auslassungspunkte (<tt>\u2026</tt>) am Ende gekennzeichnet.
	 * @param toWrap Der zuumbrechende Text.
	 * @param maxChars Die maximale Anzahl an Zeichen pro Zeile.
	 * @param maxLines Die maximale Anzahl an Zeilen, die der umbrochene Text haben soll.
	 * @return Den umbrochenen Text oder <tt>null</tt>, wenn <tt>toWrap == null</tt>.
	 * @throws IllegalArgumentException Wenn <tt>maxChars <= 0</tt>.
	 */
	public static String wrap(final String toWrap, final int maxChars, final int maxLines) {
		if (maxChars <= 0)
			throw new IllegalArgumentException("maxChars was " + maxChars + " but must be > 0");
		if (toWrap == null)
			return null;
		final String[] words = toWrap.split(SPACE + "+");
		final StringBuilder builder = new StringBuilder();
		int lineLength = 0;
		int lineNumber = 0;
		for (int i = 0; i < words.length; i++) {
			if (lineLength == maxChars) {
				if (lineNumber < maxLines - 1) {
					builder.append(NEW_LINE);
					lineNumber++;
					lineLength = 0;
				} else {
					builder.deleteCharAt(builder.length() - 1);
					builder.append(ELLIPSIS);
					break;
				}
			}
			final String currentWord = words[i];
			final int wordLength = currentWord.length();
			if (wordLength + lineLength < maxChars) {
				if (lineLength != 0) {
					builder.append(SPACE);
					lineLength++;
				}
				builder.append(currentWord);
				lineLength += wordLength;
			} else {
				if (lineLength == 0) {
					if (wordLength == maxChars) {
						builder.append(currentWord);
						lineLength = wordLength;
					} else {
						builder.append(currentWord.substring(0, maxChars));
						words[i] = currentWord.substring(maxChars);
						lineLength = maxChars;
						i--;
					}
				} else {
					if (wordLength <= maxChars) {
						if (lineNumber < maxLines - 1) {
							builder.append(NEW_LINE);
							lineNumber++;
							builder.append(currentWord);
							lineLength = wordLength;
						} else {
							final int difference = maxChars - lineLength;
							if (difference > 1) {
								builder.append(SPACE);
								builder.append(currentWord.substring(0, maxChars - lineLength - 2));
							}
							builder.append(ELLIPSIS);
							break;
						}
					} else {
						if (maxChars - lineLength > 1) {
							builder.append(SPACE);
							lineLength++;
							final int cutIndex = maxChars - lineLength;
							builder.append(currentWord.substring(0, cutIndex));
							words[i] = currentWord.substring(cutIndex);
						}
						lineLength = maxChars;
						i--;
					}
				}
			}
		}
		return builder.toString();
	}
}
```
Ist schon recht viel Code für so eine kleine Aufgabe. Geht bestimmt auch kürzer.

Gibt auch einen String zurück und nicht eine Liste. Aber vielleicht hilfts ja weiter.

Testfälle gibts auch, nur hätten diese die maximale Zeichenanzahl pro Beitrag gesprengt. :noe:
Falls daran Interesse besteht, kann ich sie in einem weiteren Beitrag ergänzen.


----------



## turmaline (6. Feb 2012)

Spacerat hat gesagt.:


> Als erstes [c]new StringBuilder(completeString);[/c] und dann darauf die ganzen [c]substring()[/c] Methoden aufrufen. Vorher evtl. noch alle Leerzeichen daraus entfernen.



warum StringBuilder verwenden?


----------



## turmaline (6. Feb 2012)

xehpuk hat gesagt.:


> Wieso ist der Teil nutzlos?
> 
> Ich stand auch mal vor der Problemstellung und habe mir etwas selbst geschrieben.



danke für den Code, ich kenne Deinen Anwendungsfall, den hatte ich auch. Ist in meinem Fall ein wenig anders aber Deinen Code werde ich evtl. auch gebrauchen.


----------



## Paddelpirat (6. Feb 2012)

xehpuk hat gesagt.:


> Wieso ist der Teil nutzlos?



Oh, sorry mein Fehler. Ich hatte in Erinnerung, dass die Methode trim() alle Leerstellen aus dem String entfernt. Von daher meinen vorherigen Post einfach ignorieren


----------



## turmaline (6. Feb 2012)

Paddelpirat hat gesagt.:


> Oh, sorry mein Fehler. Ich hatte in Erinnerung, dass die Methode trim() alle Leerstellen aus dem String entfernt. Von daher meinen vorherigen Post einfach ignorieren



Ähm, nicht alle, nur die die zu viel sind, zB am Anfang oder am Ende. In meinem Fall versuche ich die Wörter nicht zu brechen wenn es geht, deshalb "der Teil".


----------



## turmaline (6. Feb 2012)

Einen kleinen Fehler habe ich bei mir gefunden, das mit Leerzeichne ist natürlich nur dann relevant, wenn die Stringlänge größer als n ist.


```
public static List<String> getSubStrings(String text, int n) 
    {
		 List<String> list = new ArrayList<String>();
	        String s = new String(text);
	        while(s.length() != 0)
	        {
	            s = s.trim();
	            int end = n < s.length() ? n : s.length();
	            String sub = s.substring(0, end);
	            int nextIndex = end;
	            int lastIndexOfSpace = sub.lastIndexOf(" "); // index of space character
	            if(n < s.length() && lastIndexOfSpace > 0)
	            {
	                end = lastIndexOfSpace;
	                sub = s.substring(0, end);
	                nextIndex = end + 1;
	            }
	            
	            list.add(sub);
	            s = s.substring(nextIndex);
	        }
	        return list;
    }
```

Oder sieht Ihr noch andere Probleme? Bitte posten.


----------



## Spacerat (7. Feb 2012)

In StringBuilder gibt es die Methode [c]deleteCharAt()[/c]. Damit lassen sich relativ einfach alle überflüssigen Whithespaces zwischen den Wörtern entfernen - würde andauerndes [c]trim()[/c] ersetzen. Die Methode [c]substring()[/c] gibt es dort auch. Bei nochmaligem überlegen aber ist die Quelle der Ergebnisstrings aber egal, sofern du [c]String s = text;[/c] schreibst (obwohl, du könntest auch in der Methode [c]text[/c] selbst anstelle von [c]s[/c] verwenden, würdest damit aber gegen stilistische Konformitäten verstossen) aber um himmels Willen nicht [c]String s = new String(text);[/c] denn damit legst du im Zweifelsfall [c]text[/c] ein zweites mal im Heap an, statt nur die Start- und End-Pointer des Originals zu verwenden. Okay, StringBuilder verwendet zwar auch den Heap, jedoch nur vorübergehend. Dein [c]s[/c] dagegen bleibt dort, weil es im weiteren Programmverlauf durch die Instanzen der einzelnen Wörter noch verwendet wird.
Fazit: Beste Lösung - [c]String s = text;[/c]


----------



## turmaline (9. Feb 2012)

Spacerat hat gesagt.:


> In StringBuilder gibt es die Methode [c]deleteCharAt()[/c]. Damit lassen sich relativ einfach alle überflüssigen Whithespaces zwischen den Wörtern entfernen - würde andauerndes [c]trim()[/c] ersetzen. Die Methode [c]substring()[/c] gibt es dort auch. Bei nochmaligem überlegen aber ist die Quelle der Ergebnisstrings aber egal, sofern du [c]String s = text;[/c] schreibst (obwohl, du könntest auch in der Methode [c]text[/c] selbst anstelle von [c]s[/c] verwenden, würdest damit aber gegen stilistische Konformitäten verstossen) aber um himmels Willen nicht [c]String s = new String(text);[/c] denn damit legst du im Zweifelsfall [c]text[/c] ein zweites mal im Heap an, statt nur die Start- und End-Pointer des Originals zu verwenden. Okay, StringBuilder verwendet zwar auch den Heap, jedoch nur vorübergehend. Dein [c]s[/c] dagegen bleibt dort, weil es im weiteren Programmverlauf durch die Instanzen der einzelnen Wörter noch verwendet wird.
> Fazit: Beste Lösung - [c]String s = text;[/c]




```
String s = text;
```

mit dem selben erfolg kann man auch den [c]text[/c] selbst verwenden, da ich durch [c]String s = text;[/c] lediglich eine Referenz auf [c]text[/c] erstelle und ändere somit das objekt das ich übergeben bekomme...
was sagst Du dazu? ich hoffe wir reden nicht komplet an einander vorbei---


----------



## AngryDeveloper (9. Feb 2012)

turmaline hat gesagt.:


> da ich durch [c]String s = text;[/c] lediglich eine Referenz auf [c]text[/c] erstelle und ändere somit das objekt das ich übergeben bekomme...


String ist immutable. Du änderst an dem übergebenen Objekt wenn es ein String ist gar nichts.


----------



## bygones (9. Feb 2012)

wenn ich es richtig verstehe muesste doch deine Ausgabe folgendes sein

```
[lalal, la, lalal, l, ll, lalal, lalla, lhzh]
```
 (ich geb einfach die liste aus)
du hast aber 

```
[lalal, la, lalal, l ll, lalal, lalla, lhzh]
```
 - jdf beim code vom ersten post.

unter der annahme ersteres ist richtig:

```
public static List<String> getSubStrings(String text, int n) {
        String pat = "\\w{1," + n + "}";
        Pattern pattern = Pattern.compile(pat);
        Matcher matcher = pattern.matcher(text);
        List<String> s = new ArrayList<String>();
        while(matcher.find()) {
            s.add(matcher.group());
        }
        return s;
    }
```


----------



## Spacerat (9. Feb 2012)

turmaline hat gesagt.:


> ... kann man auch den [c]text[/c] selbst verwenden ...


So stand es bereits in meinem ersten Post und genau so war es auch gemeint. Unter Beachtung von Angry Developers Behauptung, die im übrigen vollkommen korrekt ist, ist es auch nicht wild, wenn [c]text[/c] innerhalb der Methode neu zugewiesen wird. Ist aber wg. stilistisch nicht wiklich korrekt, weil man schlicht einen Übergabe-Parameter verändert und seinen Wert innerhalb der Methode deswegen verliert. Nach Ausserhalb der Methode aber, wird [c]text[/c] nicht verändert.


----------



## turmaline (13. Feb 2012)

bygones hat gesagt.:


> wenn ich es richtig verstehe muesste doch deine Ausgabe folgendes sein
> 
> ```
> [lalal, la, lalal, l, ll, lalal, lalla, lhzh]
> ...



nein der string wird nicht nach jedem leerzeichen gebrochen sondern nur in dem fall wenn der string bis zum nächsten leerzeichen größer als vorgegeben ist (weiß nicht ob ich mich klar ausgedrückt habe..)



bygones hat gesagt.:


> du hast aber
> 
> ```
> [lalal, la, lalal, l ll, lalal, lalla, lhzh]
> ...



ja so ist es richtig



bygones hat gesagt.:


> unter der annahme ersteres ist richtig:
> 
> ```
> public static List<String> getSubStrings(String text, int n) {
> ...



danke, ich probiere es aus


----------



## bygones (13. Feb 2012)

turmaline hat gesagt.:


> nein der string wird nicht nach jedem leerzeichen gebrochen sondern nur in dem fall wenn der string bis zum nächsten leerzeichen größer als vorgegeben ist (weiß nicht ob ich mich klar ausgedrückt habe..)


ok dann macht mein code bsp etwas anderes, er bricht bei jedem Leerzeichen, egal wie lange der String da ist


----------

