Ausgabe in Spalten wie beim Befehl "ls"

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.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@snafu:
Für prettytable mußt Du doch vorher schon die Spaltenaufteilung kennen, oder?
Für csv ist das eine schöne Sache, mit curses kannst Du gleich mal Multiplan nachbauen ;)

@mkesper:
Ja, das geht mit locale, liefert aber meines Wissens nach nur eine der möglichen Sortiernormen.
Wobei ich das dann eher mit sorted(['zum', 'sortieren'], key=locale.strxfrm) machen würde.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Sorry, ich hab diesen Beitrag wohl übersehen...
jerch hat geschrieben:@snafu:
Für prettytable mußt Du doch vorher schon die Spaltenaufteilung kennen, oder?
Nö, man jagt die Inhalte in die Klasse und hofft, dass die Terminalemulation breit genug für eine vernünftige Ausgabe ist. ;P
jerch hat geschrieben:Für csv ist das eine schöne Sache, mit curses kannst Du gleich mal Multiplan nachbauen ;)
Naja, von ``curses`` hab ich auch nicht gesprochen.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

snafu hat geschrieben:Nö, man jagt die Inhalte in die Klasse und hofft, dass die Terminalemulation breit genug für eine vernünftige Ausgabe ist. ;P
Was macht prettytable denn mit einer Liste? Hatte es mir nicht näher angeschaut...
snafu hat geschrieben:Naja, von ``curses`` hab ich auch nicht gesprochen.
Da hab ich wohl die Ironietags vergessen ;) ;)

Grüße jerch
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ach, ich hab dich vermutlich missverstanden. Ja, du musst vorher Feldnamen vergeben, womit für PT dann auch die Anzahl der Spalten fest steht.

Insgesamt ist die Bedienung im Detail aber doch nicht so ganz das, was man von einem guten Tool erwarten würde. Gefühlt bestehen außerdem mindestens 50% des Codes aus "privaten" Getter-/Setter-Funktionen & Fehlerbehandlung. Irgendwie ist das alles schon etwas wirsch gemacht.

Der Autor hat das Projekt auch circa ein 3/4 Jahr nicht mehr angefasst, obwohl den "Issues" nach zu urteilen durchaus ein paar Leute Interesse an dem Projekt haben. Mal sehen, ob sich da noch was tut. Die Standardausgabe finde ich jedenfalls ganz nett gemacht für Fälle, wo man sich mal schnell einen Überblick über eine tabellenartige Struktur verschaffen möchte.
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

@sma, HWK:

Ich weiß nicht, ob das Absicht ist, aber eure Programme liefern nicht immer die geringste Zeilenanzahl. Versucht es mal mit

Code: Alles auswählen

['x' * 14] * 5 + ['x' * 6] * 11
, da kommen bei euch 6 Zeilen raus:

Code: Alles auswählen

xxxxxxxxxxxxxx  xxxxxx  xxxxxx
xxxxxxxxxxxxxx  xxxxxx  xxxxxx
xxxxxxxxxxxxxx  xxxxxx  xxxxxx
xxxxxxxxxxxxxx  xxxxxx  xxxxxx
xxxxxxxxxxxxxx  xxxxxx  xxxxxx
xxxxxx                        
Es geht aber auch mit 5 Zeilen, nämlich:

Code: Alles auswählen

xxxxxxxxxxxxxx  xxxxxx  xxxxxx  xxxxxx
xxxxxxxxxxxxxx  xxxxxx  xxxxxx        
xxxxxxxxxxxxxx  xxxxxx  xxxxxx        
xxxxxxxxxxxxxx  xxxxxx  xxxxxx        
xxxxxxxxxxxxxx  xxxxxx  xxxxxx
(Das liegt daran, dass es zwei Möglichkeiten mit 4 Spalten gibt, und ihr untersucht in diesem Fall nur die, die nicht klappt...)

Außerdem brecht ihr ab, sobald es nicht mehr klappt, das führt bei

Code: Alles auswählen

['x', 'x', 'x' * 20, 'x' * 20, 'x', 'x']
zu

Code: Alles auswählen

x                   
x                   
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
x                   
x   
, obwohl

Code: Alles auswählen

x  xxxxxxxxxxxxxxxxxxxx  x
x  xxxxxxxxxxxxxxxxxxxx  x
möglich ist.
So, jetzt aber der Code (im Nachhinein eigentlich straight-forward :-) )

Code: Alles auswählen

def table(seq, sep="  ", line_len=40):
    num_items = len(seq)
    col_lens = sorted(set(num_items // (i + 1) for i in range(num_items)))

    for col_len in col_lens:
        cols = [seq[i:i + col_len] for i in range(0, num_items, col_len)]
        col_widths = [max(map(len, col)) for col in cols]
        width = sum(col_widths) + len(sep) * (len(cols) - 1)
        if width < line_len:
            break
    else:
        raise ValueError("Won't fit")

    cols[-1].extend([""] * (col_len - len(cols[-1])))
    cols = [[item.ljust(w) for item in col] for w, col in zip(col_widths, cols)]
    rows = zip(*cols)
    return "\n".join(sep.join(row) for row in rows)
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Allerdings hat auch deine Version leider so ihre Schwächen:

Code: Alles auswählen

In [1]: import os; from columnize import columnize

In [2]: print columnize(os.listdir('.'))
------> print(columnize(os.listdir('.')))
test.pyc              teleopti.py~               plan.py
simplecsv.py          Desktop                           
Vorlagen              schedule.py~                      
Videos                bin                               
.nautilus             schedule.png                      
.xsession-errors      .local                            
.macromedia           schedule-ie.png                   
test.csv              columnize.py~                     
.pulse-cookie         .xine                             
[...]
Grundlage war das. Es folgte tatsächlich noch eine ganze Reihe weiterer Zeilen und in der dritten Spalte dann die einzelne Datei. Ziel ist aber, eine optimale Aufteilung, d.h. möglichst gleich große Spalten, zu haben. Natürlich immer unter Beachtung der Maximalbreite.
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

snafu hat geschrieben:Ziel ist aber, eine optimale Aufteilung, d.h. möglichst gleich große Spalten, zu haben. Natürlich immer unter Beachtung der Maximalbreite.
Oh. Ich vermutete, das Ziel war möglichst wenig Zeilen zu gebrauchen, und dass nur die letzte Spalte kürzer sein soll. Da kenne ich "ls" wohl nicht gut genug - habe keinen GNU zur Hand...

Wie ist denn genau das Kriterium? Jede Spalte höchstens so lang wie die vorhergehende, und jede höchstens eins kürzer als die erste? Dann ist das wohl was ganz anderes...
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Das Kriterium ist, möglichst gleich viele Zeilen mit den Spalten zu verbrauchen. Nur die letzte kann weniger haben, sollte aber eben nicht zu krass rausstechen. -1/-2 wäre optimal.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Um das nochmal zu verdeutlichen:

Code: Alles auswählen

urx@murx:~/test$ touch a b c
urx@murx:~/test$ ls
a  b  c
urx@murx:~/test$ touch a b c d e f g h i j k l m n o p q r s t u v w x y z
urx@murx:~/test$ ls
a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z
urx@murx:~/test$ touch a1
urx@murx:~/test$ ls
a   b  d  f  h  j  l  n  p  r  t  v  x  z
a1  c  e  g  i  k  m  o  q  s  u  w  y
Die Priorität liegt also bei gleich großen Spalten, selbst wenn dadurch nicht der komplette Bildschirm ausgefüllt wird. Zunächst werden die Namen nebeneinander sortiert und sobald mehrere Zeilen nötig werden, geschieht die Sortierung spaltenweise, d.h. untereinander.

Wer `touch` nicht kennt: `touch` legt Dateien an bzw erneuert den Zeitstempel bei vorhandenen Dateien.
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

snafu hat geschrieben:Die Priorität liegt also bei gleich großen Spalten, selbst wenn dadurch nicht der komplette Bildschirm ausgefüllt wird.
Ja, in dem Beispiel ist das einfach; das haben glaube ich alle vorgestellten Programme auch so gemacht.
Interessant wirds z.B. bei sowas: Was passiert bei 17 Dateien, wenn 6 nicht mehr nebeneinander passen, aber 5?
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Da macht ls: 4x4 (also gleich große) + 1 Zeile in der letzten Spalte. Okay, 6+6+5 wäre auch gegangen. Die Priorität ist wohl eher, möglichst wenig Zeilen zu verbrauchen und nur bei der letzten Spalte eine andere Zeilenzahl zu haben.
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

Ok, dann müsste eigentlich passen, was columnize macht.
Schafft ls in dem von dir zitierten Fall, wo columnize die echt ziemlich unschöne Ausgabe liefert, was vernünftiges? Oder gehen dir die blöden Fragen auf die Nerven ? :wink:

Ich find's aber ganz interessant... :-)
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich find's auch interessant, sonst hätt ich den Thread nicht gemacht... Ich habe die Ausgabe von deiner Funktion direkt im selben Verzeichnis mit ls verglichen und ls bringt in gewissen Fällen immer mal eine Spalte mehr unter als deine Funktion. Zum ausgiebigen Testen solltest du dir wohl eine Windows-Version von ls installieren, da das Frage-Antwort-Spiel zwar nicht direkt nervig, aber doch recht ineffektiv ist. ;)

Ich habe jetzt mal eine objektorientierte Implementierung angefangen, die den Vorstellungen aber bisher nur rudimentär entspricht. Es ging mir dort erstmal um die Lesbarkeit und weniger um Geschwindigkeit, da ich durch die bisher gezeigten Funktionen kaum durchgeblickt habe und sie auch nicht sonderlich schön fand. Das habe ich bis jetzt:

Code: Alles auswählen

from curses import setupterm, tigetnum

class Columnizer(object):
    def __init__(self, indent=2, width=None):
        self.spacer = indent * ' '
        self.width = width

    def drop_overage(self, strings):
        if self.width is None:
            setupterm()
            width = tigetnum('cols')
        else:
            width = self.width

        while len(self.spacer.join(strings)) > width:
            strings = strings[:-1]

        return strings

    def make_lines(self, strings):
        lines = []

        while strings:
            remaining = self.drop_overage(strings)
            lines.append(remaining)
            strings = strings[len(remaining):]

        return lines
Werde das erstmal so belassen, da ich morgen leider früh raus muss. TODO ist halt, die Spaltenbreite einheitlich zu bekommen unter der Beachtung, dass einzelne Elemente dann in die nächste Spalte rutschen können und sich damit wieder alles nachfolgende ändert. Wenn man das Glück hat, dass der längste Eintrag ziemlich am Schluss ist, hat man sicher noch mehr Spass. :)
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

Das Verhalten von ls ist mir zu ungenau spezifiziert. Ich glaube, es wäre sinnvoller, wenn du einfach deine eigenen Prioritäten umsetzt, als genau auf ls zu achten.

(Wie ls allerdings mehr Spalten unterbringen kann als columnize, verstehe ich nicht.)
Antworten