C# :: Aufgabe #62 :: Lösung #1

1 Lösung Lösung öffentlich
#62

Spaltenbreite und Blocksatz

Anfänger - C# von bibir - 03.09.2014 um 08:33 Uhr
Ein vorgegebener Text soll eingelesen und in einer bestimmten Spaltenbreite wieder ausgegeben werden.
Die Spaltenbreite N wird als Eingabeparameter vorgegeben. Der Text besteht aus "Wörtern", die durch Leerzeichen (Blank) getrennt sind. Unter einem "Wort" wird hier eine beliebige zusammenhängende Zeichenkette verstanden, wobei jedes Zeichen eine Druckstelle einnimmt.

Der Text soll ausgerichtet werden an den beiden Spaltenrändern, und die zusätzlich einzuführenden Blanks sollen möglichst gleichmäßig auf die Zwischenräume zwischen den Wörtern verteilt werden.

Der Text beginnt in einer Spalte grundsätzlich linksbündig, Wörter, die länger sind als die Spaltenbreite, werden am rechten Rand ohne Trennzeichen zwangsweise getrennt. Silbentrennung darf nicht durchgeführt werden.
#1
vote_ok
von eulerscheZhl (5230 Punkte) - 24.11.2014 um 20:19 Uhr
Optimierungskriterien (hierarchisch geordnet):
1. So wenig Zeilenumbrüche wie möglich
2. So wenig Leerzeichen am Ende einer Zeile wie möglich (keine kurzen Worte allein in eine Zeile schreiben)
3. So wenig aufeinanderfolgende Leerzeichen wie möglich
Da gibt es eine Menge Fälle zu prüfen, das Programm ist leider zeimlich langsam. Verbesserungsvorschläge sind willkommen.

Quellcode ausblenden C#-Code
using System;
using System.Text;
using System.Collections.Generic;

namespace trainYourProgrammer
{
	class MainClass
	{
		static string FormatColumns(string text, int textWidth, int space)
		{
			string[] lines = FormatText (text, textWidth).Split ('\n');
			int columnCount = (Console.WindowWidth + space) / (textWidth + space);
			int lineCount = (lines.Length + columnCount - 1) / columnCount;
			StringBuilder result = new StringBuilder ();
			for (int line = 0; line < lineCount; line++) {
				for (int column = 0; column < columnCount; column++) {
					if (line + column * lineCount < lines.Length)
						result.Append (lines [line + column * lineCount]);
					if (column < columnCount - 1)
						result.Append (' ', space - 0);
				}
				result.AppendLine ();
			}
			return result.ToString ();
		}

		static string FormatText(string text, int textWidth)
		{
			string[] words = text.Split (new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			StringBuilder result = new StringBuilder ();
			List<string> blockWords = new List<string> ();
			for (int i = 0; i < words.Length; i++) {
				if (words [i].Length % textWidth == 0 || words [i].Length % textWidth == textWidth - 1) {
					result.Append (FormatBlock (blockWords, textWidth));
					blockWords.Clear ();
				}
				blockWords.Add (words [i]);
			}
			result.AppendLine (FormatBlock (blockWords, textWidth));
			return result.ToString ();
		}

		static string FormatBlock(List<string> words, int textWidth)
		{
			int [] wordLength = new int[words.Count];
			for (int i = 0; i < wordLength.Length; i++)
				wordLength [i] = words [i].Length % textWidth;
			int[] wordStart = new int[wordLength.Length];
			int linesNeeded = FillFromBeginning (wordLength, ref wordStart, textWidth, 0, 0);
			int optimumVal = Review (wordLength, wordStart, textWidth);
			FitBlockRecurs (wordLength, ref optimumVal, ref wordStart, ref wordStart, linesNeeded, textWidth, 0);
			//optimale Lösung in Zeilen zerlegen und diese für sich optimieren
			StringBuilder result = new StringBuilder ();
			List<string> currWords = new List<string> ();
			for (int i = 0; i < words.Count; i++) {
				if (wordStart [i] % textWidth == 0) { //Zeilenanfang
					result.Append (FitLine(currWords, textWidth));
					currWords.Clear ();
				}
				currWords.Add (words [i]);
			}
			result.Append (FitLine(currWords, textWidth)); //und das letzte Wort nicht vergessen
			return result.ToString ();
		}

		public static string FitLine(List<string> words, int textWidth)
		{
			if (words.Count == 0)
				return "";
			string result = String.Join (" ", words);
			int remainingSpaces = (textWidth * result.Length - result.Length) % textWidth;
			result = words [0];
			for (int i = 1; i < words.Count; i++) {
				result += " "; //normales Leerzeichen zwischen Wörtern
				result += new string (' ', remainingSpaces / (words.Count - i)); //Füllzeichen
				remainingSpaces -= remainingSpaces / (words.Count - i);
				result += words [i];
			}
			if (words.Count == 1)
				result += new string (' ', remainingSpaces);
			for (int i = textWidth; i < result.Length; i+= textWidth + 1)
				result = result.Insert (i, "\n");
			return result + "\n";
		}

		public static int FillFromBeginning(int[] wordLength, ref int[] wordStart, int textWidth, int cursorIndex, int wordIndex)
		{
			for (int i = wordIndex; i < wordLength.Length; i++) {
				if ((cursorIndex + wordLength [i] - 1) / textWidth > cursorIndex / textWidth) //muss in nächste Zeile schreiben
					cursorIndex = cursorIndex / textWidth * textWidth + textWidth;
				wordStart [i] = cursorIndex;
				cursorIndex += wordLength [i];
				if (cursorIndex % textWidth != 0) //nicht von selbst in nächster Zeile angekommen
					cursorIndex++; //Leerzeichen einfügen
			}
			return (cursorIndex + textWidth - 1) / textWidth;
		}

		public static void FitBlockRecurs (int[] wordLength, ref int optimumVal, ref int[] currSol, ref int[] optimumSol, int linesNeeded, int textWidth, int lastMoved)
		{
			for (int move = lastMoved + 1; move < wordLength.Length; move++) {
				int[] testSolution = new int[wordLength.Length];
				for (int i = 0; i < move; i++)
					testSolution [i] = optimumSol [i];
				//schreibe words[move] in nächste Zeile
				int index = optimumSol [move - 1] / textWidth * textWidth + textWidth;
				if (FillFromBeginning (wordLength, ref testSolution, textWidth, index, move) > linesNeeded)
					continue; //es werden mehr Zeilen belegt, als nötig, Abbruch
				int currVal = Review (wordLength, testSolution, textWidth);
				if (currVal < optimumVal) { //eine bessere Lösung als die bisherige wurde gefunden
					optimumVal = currVal;
					optimumSol = testSolution;
				}
				FitBlockRecurs (wordLength, ref optimumVal, ref testSolution, ref optimumSol, linesNeeded, textWidth, move);
			}
		}

		public static int Review(int[] wordLength, int[] wordStart, int textWidth) //Bewertung der Aufteilung. Je kleiner der Wert, desto besser
		{
			int result = 0;
			for (int i = 0; i < wordStart.Length; i++) {
				if (wordStart [i] % textWidth == 0) {//Zeilenanfang
					int wordInLine = 1;
					for (int j = i+1; j < wordStart.Length; j++)
						if (wordStart [i] / textWidth == wordStart [j] / textWidth)
							wordInLine++;
					if (wordInLine == 1)
						result += (textWidth - wordLength [i]) * 100; //100 Strafpunkte für jedes Leerzeichen am Zeilenende
					else { //mehrere Wörter in einer Zeile -> (Leerzeichengruppenlaenge -1)^2 Strafpunkte --> ".." ist ein Strafpunkt, "..." sind 4 Punkte, ...
						int remainingSpaces = (textWidth - wordStart [i + wordInLine - 1]) % textWidth;
						int maxGroundLength = 0;
						if (remainingSpaces > 0)
							maxGroundLength = (wordInLine - 1) / remainingSpaces;
						result += maxGroundLength * maxGroundLength;
					}
				}
			}
			return result;
		}

		static void Main(string[] args)
		{
			string text = "Dies ist ein Typoblindtext. An ihm kann man sehen, ob alle Buchstaben da sind und wie sie aussehen. Manchmal benutzt man Worte wie Hamburgefonts, Rafgenduks oder Handgloves, um Schriften zu testen. Manchmal Sätze, die alle Buchstaben des Alphabets enthalten - man nennt diese Sätze »Pangrams«. Sehr bekannt ist dieser: The quick brown fox jumps over the lazy old dog. Oft werden in Typoblindtexte auch fremdsprachige Satzteile eingebaut (AVAIL® and Wefox™ are testing aussi la Kerning), um die Wirkung in anderen Sprachen zu testen. In Lateinisch sieht zum Beispiel fast jede Schrift gut aus. Quod erat demonstrandum. Seit 1975 fehlen in den meisten Testtexten die Zahlen, weswegen nach TypoGb. 204 § ab dem Jahr 2034 Zahlen in 86 der Texte zur Pflicht werden. Nichteinhaltung wird mit bis zu 245 € oder 368 $ bestraft. Genauso wichtig in sind mittlerweile auch Âçcèñtë, die in neueren Schriften aber fast immer enthalten sind. Ein wichtiges aber schwierig zu integrierendes Feld sind OpenType-Funktionalitäten. Je nach Software und Voreinstellungen können eingebaute Kapitälchen, Kerning oder Ligaturen (sehr pfiffig) nicht richtig dargestellt werden.";
			Console.WriteLine(FormatColumns(text, 10, 4) + "\n\n");
			Console.WriteLine(FormatColumns(text, 15, 5));
		}
	}
}


Ausgabe:
Quellcode ausblenden C-Code
Dies   ist    Sätze,        auch  frem    demonstran    245 € oder    unktionali
ein   Typo    die   alle    dsprachige    dum.  Seit    368      $    täten.    
blindtext.    Buchstaben    Satzteile     1975          bestraft.     Je    nach
An     ihm    des           eingebaut     fehlen        Genauso       Software  
kann   man    Alphabets     (AVAIL®       in     den    wichtig in    und Vorein
sehen,        enthalten     and Wefox™    meisten       sind    mi    stellungen
ob    alle    -      man    are           Testtexten    ttlerweile    können    
Buchstaben    nennt         testing       die           auch          eingebaute
da    sind    diese         aussi   la    Zahlen,       Âçcèñtë,      Kapitälche
und    wie    Sätze         Kerning),     weswegen      die     in    n, Kerning
sie           »Pangrams«    um     die    nach          neueren       oder      
aussehen.     .     Sehr    Wirkung in    TypoGb.       Schriften     Ligaturen 
Manchmal      bekannt       anderen       204      §    aber  fast    (sehr     
benutzt       ist           Sprachen      ab     dem    immer         pfiffig)  
man  Worte    dieser:       zu testen.    Jahr  2034    enthalten     nicht     
wie   Hamb    The  quick    In            Zahlen  in    sind.  Ein    richtig  d
urgefonts,    brown  fox    Lateinisch    86     der    wichtiges     argestellt
Rafgenduks    jumps over    sieht  zum    Texte  zur    aber          werden.   
oder Handg    the   lazy    Beispiel      Pflicht       schwierig     
loves,  um    old   dog.    fast  jede    werden.       zu    inte    
Schriften     Oft werden    Schrift       Nichteinha    grierendes    
zu testen.    in    Typo    gut   aus.    ltung wird    Feld  sind    
Manchmal      blindtexte    Quod  erat    mit bis zu    OpenType-F    



Dies  ist   ein     ist dieser: The     demonstrandum.      immer enthalten
Typoblindtext.      quick brown fox     Seit       1975     sind.       Ein
An ihm kann man     jumps over  the     fehlen       in     wichtiges  aber
sehen, ob  alle     lazy  old  dog.     den     meisten     schwierig    zu
Buchstaben   da     Oft  werden  in     Testtexten          integrierendes 
sind  und   wie     Typoblindtexte      die     Zahlen,     Feld       sind
sie   aussehen.     auch                weswegen            OpenType-Funkti
Manchmal            fremdsprachige      nach    TypoGb.     onalitäten.  Je
benutzt     man     Satzteile           204  §  ab  dem     nach   Software
Worte       wie     eingebaut           Jahr       2034     und  Voreinstel
Hamburgefonts,      (AVAIL®     and     Zahlen  in   86     lungen   können
Rafgenduks oder     Wefox™      are     der  Texte  zur     eingebaute     
Handgloves,  um     testing   aussi     Pflicht werden.     Kapitälchen,   
Schriften           la    Kerning),     Nichteinhaltung     Kerning    oder
zu      testen.     um die  Wirkung     wird  mit   bis     Ligaturen      
Manchmal Sätze,     in      anderen     zu 245  €  oder     (sehr  pfiffig)
die        alle     Sprachen     zu     368 $ bestraft.     nicht   richtig
Buchstaben          testen.      In     Genauso wichtig     dargestellt    
des   Alphabets     Lateinisch          in         sind     werden.        
enthalten           sieht               mittlerweile        
-   man   nennt     zum    Beispiel     auch   Âçcèñtë,     
diese     Sätze     fast       jede     die in  neueren     
»Pangrams«.         Schrift     gut     Schriften  aber     
Sehr    bekannt     aus. Quod  erat     fast                

Kommentare:

Für diese Lösung gibt es noch keinen Kommentar

Bitte melden Sie sich an um eine Kommentar zu schreiben.
Kommentar schreiben
2094325

Du scheinst einen AdBlocker zu nutzen. Ich würde mich freuen, wenn du ihn auf dieser Seite deaktivierst und dich davon überzeugst, dass die Werbung hier nicht störend ist.