Zeilenumbruch programmieren

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

@EyDu Das war eine Aufforderung an dich, eine Idee beizusteuern :)

@nfehren: Das ist zu Grob. Spiele das doch mal mit einem Beispieltext durch, dann siehst du auch die einzelnen Faelle an die du denken musst.
Wie stellst du fest, dass du umbrechen musst? Wie verwaltest du die Information welche Woerter du schon ausgegeben hast und welche noch kommen, etc etc
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

cofi hat geschrieben:@EyDu Das war eine Aufforderung an dich, eine Idee beizusteuern :)
So sieht es nämlich aus... außerdem wollte ich testen, ob dem OP das auffällt, indem er die bis dato vorgeschlagenen Ideen, z.B. von /me, durchgeht...

Klingt doch glaubhaft, oder? :mrgreen:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Nagut, wenn es so ist. Eigentlich lösen wir hier ja keine Hausaufgaben, aber hier können wir vielleicht mal eine Ausnahme machen.

Code: Alles auswählen

# -*- coding: utf-8 -*-
TEXT = u"Das hier wird ein längerer Text der einem Test dienen soll, einen Zeilenumbruch zu machen. Die Zeilen dürfen 50 Zeichen breit sein und müssen danach ein newline vorweisen können. Im Nachhinein muss man das Programm so verbessern, dass es keine ganzen Wörter abschneidet. Viel Glück!"

j=" ".join;w=lambda t:"\n".join(map(j,reduce(lambda l,y:(l[-1].append(y)if 80>=len(j(l[-1]+[y]))else l.append([y]),l)[1],t.split(),[[]])))

print w(TEXT)
Man kann da sicher noch ein wenig kürzen, aber ehrlich gesagt wollte ich aus einer überischtlichen Lösung keine unübersichtliche machen. Probleme mit Wörter >80 Zeichen werden natürlich ignoriert.
Das Leben ist wie ein Tennisball.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

EyDu hat geschrieben:Man kann da sicher noch ein wenig kürzen, aber ehrlich gesagt wollte ich aus einer überischtlichen Lösung keine unübersichtliche machen.
Das ist eindeutig die absolut übersichtlichste Variante die einem als erstes in den Sinn kommt. Vielleicht mag der Fragesteller trotzdem noch mal einen anderen Ansatz ausprobieren.
nfehren
User
Beiträge: 98
Registriert: Donnerstag 31. Oktober 2013, 15:11

EyDu hat geschrieben:Nagut, wenn es so ist. Eigentlich lösen wir hier ja keine Hausaufgaben, aber hier können wir vielleicht mal eine Ausnahme machen.

Code: Alles auswählen

# -*- coding: utf-8 -*-
TEXT = u"Das hier wird ein längerer Text der einem Test dienen soll, einen Zeilenumbruch zu machen. Die Zeilen dürfen 50 Zeichen breit sein und müssen danach ein newline vorweisen können. Im Nachhinein muss man das Programm so verbessern, dass es keine ganzen Wörter abschneidet. Viel Glück!"

j=" ".join;w=lambda t:"\n".join(map(j,reduce(lambda l,y:(l[-1].append(y)if 80>=len(j(l[-1]+[y]))else l.append([y]),l)[1],t.split(),[[]])))

print w(TEXT)
Man kann da sicher noch ein wenig kürzen, aber ehrlich gesagt wollte ich aus einer überischtlichen Lösung keine unübersichtliche machen. Probleme mit Wörter >80 Zeichen werden natürlich ignoriert.
Natürlich sehr nett das du dir die Mühe machst und mir den Code-Schnipsel schreibst aber darauf war ich nicht wirklich aus.. Ich möchte die Sache verstehen. Sonst könnte ich ja auch einfach dieses textwrap modul benutzen. Das was du geschrieben hast verstehe ich zum großteil nicht. Trotzdem vielen dank für die Mühe
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

nfehren hat geschrieben:Ich möchte die Sache verstehen.
Dann habe ich hier mal einen Codeschnipsel zum experimentieren und erweitern.

Code: Alles auswählen

def get_first_part(text, max_length):
    if len(text) <= max_length:
        return text
    for pos in range(max_length - 1, 0, -1):
        if text[pos] == ' ':
            return text[:pos]
BlackJack

Ähm, um die Position eines Zeichens zu finden gibt es auch Methoden auf Zeichenketten-Objekten.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Und ich werfe jetzt mal `''.partition()` in den Ring:

Code: Alles auswählen

>>> text = 'A long sentence, really long.'
>>> text.partition(' ')
('A', ' ', 'long sentence, really long.')
Ich könnte mir vorstellen, dass man sich damit Wort für Wort geben lässt und diese zusammensetzt, bis der zusammengesetzte Text plus das kommende Wort länger als die 80 Zeichen sind. Dann geb' ich den zusammengesetzten Text zurück und mach' weiter...

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Wäre es da nicht geschickter gleich mit `split()` alle Worte auf einmal zu trennen, anstatt das Wort für Wort zu machen?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

BlackJack hat geschrieben:Ähm, um die Position eines Zeichens zu finden gibt es auch Methoden auf Zeichenketten-Objekten.
Der Fragesteller wollte ja unbedingt so low-level wie möglich arbeiten.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

BlackJack hat geschrieben:@mutetella: Wäre es da nicht geschickter gleich mit `split()` alle Worte auf einmal zu trennen, anstatt das Wort für Wort zu machen?
Man könnte dann auch noch direkt eine Liste aus Tupeln mit Wort und Wortlänge bauen.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@BlackJack
Ja, wäre geschickter. Aber ich wollte einfach mal das arme, vernachlässigte `partition()` aus seiner dunklen Kammer ans Licht bringen... :wink:

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Ich glaube einfache Lösungen werden hier nicht gesucht :P

Code: Alles auswählen

# -*- coding: utf-8 -*-
import re

TEXT = u"Das hier wird ein längerer Text der einem Test dienen soll, einen Zeilenumbruch zu machen. Die Zeilen dürfen 50 Zeichen breit sein und müssen danach ein newline vorweisen können. Im Nachhinein muss man das Programm so verbessern, dass es keine ganzen Wörter abschneidet. Viel Glück!"
print re.sub('(.{,80}) ',r'\1\n',TEXT)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

@Sirius3: Die Lösung ist wirklich hübsch.
Das Leben ist wie ein Tennisball.
nfehren
User
Beiträge: 98
Registriert: Donnerstag 31. Oktober 2013, 15:11

Also ich hab das jetzt mal soweit, dass er bei 50 abschneidet und mir sagt wie viele zeichen + das letzte Leerzeichen es noch gibt. Jetzt muss ich nur noch diese Anzahl der jeweiligen Zeile abziehen und an die untere vorne anhängen. Hier steh ich jetzt aber echt aufm Schlauch vor allem weil ich jetzt in einer Endlosschleife bin.

Code: Alles auswählen

while j <= len(s):
    line = ""
    for i in range (j -1 , j + z -1):
        if i < len(s):
            line += (s[i])
    if len(line) == z:
        for i in range(0, z):
            print(z -1 - i, len(line))
            if line[z -1 - i] == " ":
                break
        j += 50 - i -1
    print(i+1)
    print(line)
BlackJack

@nfehren: Die Zeilen 2 bis 5 (inklusive) sind äusserst umständlich. Den gleichen Effekt könntest Du durch eine einzige Zeile bekommen in der Du einfach den entsprechenden Ausschnitt per „slicing” kopierst. Also ``line = s[j - 1:j + z - 1]`` wenn ich das richtig sehe.

Aber vielleicht sollte man das Ende erst mal ermitteln statt zwingend `z` Zeichen an `line` zu binden. Ich bin mir auch nicht sicher ob ``j - 1`` so eine gute Idee ist, denn `j` wird man vor der Schleife ja an 0 binden.

Du hast da noch eine literale 50 im Quelltext die vielleicht noch durch `z` ersetzt werden sollte. Beziehungsweise alle `z` dann durch einen Namen der dem Leser auf den ersten Blick verrät was der Wert in dem Algorithmus bedeutet.

Ich würde zwei Indizes verwenden, einen für die Startposition und einen für die Endposition. Und das ausschneiden der Teilzeichenkette erst machen wenn beide stimmen. Also solange der Startindex noch kleiner als die Länge des Textes ist, den Endindex ermitteln, dann die Teilzeichenkette ausgeben und den Startindex anpassen. Start und Ende kann man auch entsprechend `start` und `end` nennen, statt `i` und `j`.
BlackJack

Ich hab's mal in JavaScript umgesetzt:

Code: Alles auswählen

#!/usr/bin/env node
'use strict';

String.prototype.wrap = function (maxWidth) {
    var lines, start, end;
    maxWidth = maxWidth || 80;
    lines = [];
    start = 0;
    while (start < this.length) {
        end = start + maxWidth;
        if (end < this.length) {
            while (end > start && this[end] !== ' ') {
                end -= 1;
            }
        }
        if (start === end) {
            end += maxWidth;
        }
        lines.push(this.slice(start, end));
        start = end;
        if (this[start] === ' ') {
            start += 1;
        }
    }
    return lines.join('\n');
};

var main = function () {
    var text = 'Das hier wird ein längerer Text der einem Test dienen soll,'
        + ' einen Zeilenumbruch zu machen. Die Zeilen dürfen 50 Zeichen breit'
        + ' sein und müssen danach ein newline vorweisen können. Im Nachhinein'
        + ' muss man das Programm so verbessern, dass es keine ganzen Wörter'
        + ' abschneidet. Viel Glück! '
        + '=================================================='
        + '==================================================';
    
    console.log(text.wrap(50));
    console.log(text.wrap());
    console.log(''.wrap());
};

if (require.main === module) {
    main();
}
Antworten