Join-Methode

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.
bahmady
User
Beiträge: 12
Registriert: Sonntag 30. Mai 2021, 15:59

@Sirius3

hier mein Code:

Code: Alles auswählen


b = ["Mark Smith", "Bobby Brown", "Sue Miller", "Jenny Igotit"]
c = ["555−1234", "555−9876","555−6743", "867−5309"]
test1 = " ".join(b)
test2 = " ".join(c)
d = '{} \n{}'.format(test1,test2) 
print(d)

Ich habe ehrlich gesagt schon alle möglichen Varianten versucht. Dies ist die aktuellste. Nun wurde mir vorher schon von mehreren Usern gesagt, dass die Kombinationen mit einer Schleife nicht das sinnvollste sei. Das Problem bei der Ausgabe meines Codes ist, dass die Zahlen nicht richtig formatiert sind (siehe vorherige Antwort). Wie man sehen kann, bin ich noch ein Anfänger im Programmieren. Mir macht es auch an sich Spaß, es ist aber ziemlich frustrierend, wenn man nicht weiterkommt -.-
bahmady
User
Beiträge: 12
Registriert: Sonntag 30. Mai 2021, 15:59

@LukeNukem

Alles klar ich setzte mich dann Abends ran. Nur... was genau ist mit einem "Formatstring" gemeint? Kannst du mir das bitte erklären, bevor ich mich dransetzte? :)

@__blackjack__

Ja das stimmt ich habe die a) und b) im Vergleich dazu recht leicht geschafft. Am Anfang der Aufgabe steht, dass vorausgesetzt wird, dass die Anzahl der Elemente in beiden Listen gleich ist. Wenn die Nummern nun länger als die Namen wären, hätte man bestimmt ein Formatierungsproblem...
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@bahmady: Mit Nummern länger als Namen ist nicht gemeint, dass es mehr Nummern als Namen gibt, sondern eine Nummer als String eine größere Länge aufweist als der zugehörige Name. Zu Deinen Beispieldaten habe ich "Lin Dan" mit der Nummer "800-8888" hinzugefügt, auf die das zutrifft.

Code: Alles auswählen

names = ["Mark Smith", "Bobby Brown", "Sue Miller", "Lin Dan", "Jenny Igotit"]
numbers = ["555−1234", "555−9876","555−6743", "800-8888", "867−5309"]
Die Ausgabe sollte dann so aussehen:

Code: Alles auswählen

Mark Smith Bobby Brown Sue Miller Lin Dan  Jenny Igotit 
555−1234   555−9876    555−6743   800-8888 867−5309
Mit Format-String ist üblicherweise eine Formatierung gemäß der formatspec Format Specification Mini-Language gemeint. Vermutlich habt ihr das bereits durchgenommen, sonst wäre die Aufgabe etwas sehr fordernd.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@bahmady: Wie ist denn bei euch die ``-d``-Option definiert? Einfacher als beim Original ``paste``? Egal ob da nun nur *ein* Zeichen übergeben werden kann, oder wie im Original mehrere Zeichen die zyklisch verwendet werden, muss das ja auch bei Deinem Code berücksichtigt werden.

Unterschiedliche Länge was die Anzahl der Zeilen der Eingabe angeht ist an sich aber auch kein grosses Problem. Das Original ``paste`` füllt die kürzeren Dateien dann einfach mit Leerzeilen auf. Da ist `itertools.zip_longest()` aus der Standardbibliothek hilfreich.

Statt Format-Strings könnte man das auch mit der `ljust()`-Methode auf Zeichenketten lösen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
bahmady
User
Beiträge: 12
Registriert: Sonntag 30. Mai 2021, 15:59

@LukeNukem

Ich verstehe immernoch nicht wie ich mit den Längenangaben einen Format-String ausgeben soll bzw. was überhaupt mit einem "Format-String" gemeint ist. Und wenn ich die beiden Format-Strings habe, wie soll ich die dann richtig formatiert untereinander ausgeben?
Egal wie ich es versuche, ich komme nicht zur Lösung (ohne die Installation von Bibliotheken). Hier ist mein aktueller Code:

Code: Alles auswählen

#texteins lesen
texteins = open('C:/Users/bah90/Desktop/t1.txt', encoding='utf-8') 
a = texteins.read()
a2 = a.splitlines(False)
texteins.close()
#testzwei lesen
textzwei = open('C:/Users/bah90/Desktop/t2.txt', encoding='utf-8')
b = textzwei.read()
b2 = b.splitlines(False)
textzwei.close()
print(a2)
print(b2)
for i in a2:
    l = len(i)
    print(l)
a2_format = d = '{} \n{}'.format(a2,b2)
print(a2_format)
Wie soll ich l verwenden? Und wie soll ich die format-Methode bitte verwenden? Ich hänge schon mehrere Tage an dieser Kleinigkeit und bin ehrlich gesagt ziemlich verzweifelt...
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@bahmady: In die Vorlage kann man deutlich mehr als einfach nur "{}" für Platzhalter rein schreiben. Das ist eine eigene kleine ”Formatbeschreibungssprache” und da wurde auch schon ein Link zur Dokumentation in diesem Thema gepostet. Man kann da unter anderem auch ausdrücken, dass das was da eingesetzt werden soll, eine bestimmte Breite haben soll, und ob das links- oder rechtsbündig in diesen Platz eingesetzt werden soll.

Wenn Du `format()` nicht verwenden möchtest, gibt es wie schon gesagt die `ljust()`-Methode auf Zeichenketten, mit der man auch eine angegebene Breite pro Spaltenwert erreichen kann.

Dir nützt es nichts *ein* `l` am Ende einer Schleife zu haben, und zwar das der letzten Spalte. Du brauchst für jede Spalte den Wert der Breite der Spalte.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

bahmady hat geschrieben: Dienstag 1. Juni 2021, 10:10 Ich verstehe immernoch nicht wie ich mit den Längenangaben einen Format-String ausgeben soll bzw. was überhaupt mit einem "Format-String" gemeint ist. Und wenn ich die beiden Format-Strings habe, wie soll ich die dann richtig formatiert untereinander ausgeben?
Ein Formatstring ist quasi eine Vorschrift zur Formatierung. Da ein Beispiel oft mehr sagt als tausend Worte:

Code: Alles auswählen

eins = 'eins'
formatstring = 'a{!r:>30s}e'
print(formatstring.fomat(eins)) # huhu
Ausgabe:

Code: Alles auswählen

a                        'eins'e
Schau mal in die offizielle Dokumentation unter [1] und in die dortigen Verweise, und für eine etwas weniger formale Darstellung bei PYMOTW [2] und GeeksForGeeks [3] und [4].
bahmady hat geschrieben: Dienstag 1. Juni 2021, 10:10 Wie soll ich l verwenden?
Als Variablennamen am Besten gar nicht. Bitte versteh' mich nicht falsch, für kleine Demo- und Wegwerfskripte habe ich keine sonderlich großen Schwierigkeiten mit Variablennamen, die aus nur einem Buchstaben bestehen, aber I, l, 1 und i lassen sich je nach Schriftart leider kaum unterscheiden und sind deswegen schwer zu lesen.
bahmady hat geschrieben: Dienstag 1. Juni 2021, 10:10 Und wie soll ich die format-Methode bitte verwenden?
Wie ich oben in meinem 'huhu'-Beispiel: mit einem Formatstring. ;-)

Du, sag' mal, wann mußt Du Deine Hausarbeit eigentlich fertig haben?


[1] https://docs.python.org/3/library/string.html
[2] https://pymotw.com/3/string/
[3] https://realpython.com/python-formatted-output/
[4] https://www.geeksforgeeks.org/python-format-function/
bahmady
User
Beiträge: 12
Registriert: Sonntag 30. Mai 2021, 15:59

@LukeNukem

Ich kann meine Hausarbeit abgeben wann ich will. Diese Aufgabe ist die 4.) von 6 Aufgaben mit mehreren Aufgabenteilen. Ich wollte aber nächste Woche mit Datenbanken anfangen (ich mache ein Fernstudium). Ich habe nun mehrere Stunden alles Mögliche ausprobiert, habe in der Dokumentation für die format-Funktion reingeschaut, nichts klappt. Es wäre echt nett wenn du oder ein anderer mir einfach zeigen würde, wie ich die Format-Funktion hier richtig einsetzte. Ich habe soooo viel Zeit in dem kleinen Aufgabenteil investiert, da wird schon ein Lerneffekt da sein!
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@bahmady: Dann lass das mit `format()` und verwende `ljust()`. Was Du bisher gezeigt hast, deutet aber auf sehr viel grundlegendere Probleme bei der Planung und Strukturierung von Code hin. Das ist durch das verstehen von `format()` nicht getan, das ist nur ein Detailproblem, von etwas was vom Konzept her in dem bisher gezeigten noch nicht gelöst ist. Und es sieht so ein bisschen so aus, als würdest Du versuchen das durch herumprobieren angehen zu wollen.

Beschreibe doch mal in Worten was Du machen müsstest, und in welcher Reihenfolge, wenn Du das manuell auf Kästchenpapier lösen wolltest, das die Spalten so breit sind wie die Zeilen/Werte in der ersten Datei (plus Trennzeichen).

(Wobei ich die Aufgabenstellung immer noch verwirrend finde, denn sie hält sich nicht an das verhalten von ``paste`` und scheint so formuliert, das die Zeilen in Datei 1 grundsätzlich länger sein müssen als die korrespondierenden Zeilen in Datei 2.)
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

bahmady hat geschrieben: Dienstag 1. Juni 2021, 15:40 @LukeNukem

Ich kann meine Hausarbeit abgeben wann ich will. Diese Aufgabe ist die 4.) von 6 Aufgaben mit mehreren Aufgabenteilen. Ich wollte aber nächste Woche mit Datenbanken anfangen (ich mache ein Fernstudium). Ich habe nun mehrere Stunden alles Mögliche ausprobiert, habe in der Dokumentation für die format-Funktion reingeschaut, nichts klappt. Es wäre echt nett wenn du oder ein anderer mir einfach zeigen würde, wie ich die Format-Funktion hier richtig einsetzte. Ich habe soooo viel Zeit in dem kleinen Aufgabenteil investiert, da wird schon ein Lerneffekt da sein!
Najaaa... im Prinzip willst Du ja zwei Dateien lesen, deren Dateinamen auf der Kommandozeile übergeben werden sollen, dann für jede Zeile n die maximale Länge max([len(strings in n. Zeile der Dateien)]) ermitteln und daraus einen Formatstring bauen. Also eine Vorschrift, wie breit das betreffende Feld ausgegeben werden soll.

Der erste Schritt ist also, die Parameter zu parsen, die auf der Kommandozeile übergeben wurden. Wie für so vieles, hat Python dafür natürlich eine (eigentlich sogar mehrere) eingebaute Module, ich persönlich verwende dafür gerne das Modul "argparse", das die Klasse "ArgumentParser" bereitstellt -- der dann natürlich entsprechend konfiguriert werden muß. Und das könnte für Deinen Anwendungsfall etwa so aussehen:

Code: Alles auswählen

from argparse import ArgumentParser

if __name__ == '__main__':
    parser = ArgumentParser(description='mimic paste(1)')
    parser.add_argument('filenames', nargs='+', help='the files')
    parser.add_argument('--serial', '-s', action='store_true', help='output as columns')
    args = parser.parse_args()
Wenn Dein Programm mit "./paste.py -s names.txt numbers.txt" aufgerufen wurde, dann haben die Eigenschaften von "args" hinterher folgende Werte:

Code: Alles auswählen

args.filenames = ["names.txt", "numbers.txt"]
args.serial = True
Okay, wir haben jetzt eine Liste mit Dateinamen und wollen die Inhalte dieser Datein gerne in zeilenweise Listen einlesen:

Code: Alles auswählen

contents = list()
for filename in args.filenames: 
    with open(filename, 'r') as ifh: # diesen abgekürzten Variablennamen mögen manche hier nicht, ich schon ;-)
        content = ifh.read().splitlines() # das entfernt im Gegensatz zu ifh.readlines() auch die Zeilenumbrüche
        contents.append(content)
Unsere Variable "contents" enthält jetzt also eine Liste, deren einzelne Elemente wiederum Listen sind, also etwa so:

Code: Alles auswählen

contents = [
    ["Mark Smith", "Bobby Brown", ...],
    ["555-1234", "555-9876", ...]
]
Jetzt wollen wir natürlich zuallererste etwas machen, das jeder Autor immer, und zwar absolut immer, machen sollte: nämlich unsere Eingaben validieren, in diesem Falle also: sicherstellen, daß alle Elemente der Liste "contents" dieselbe Länge, hier also: dieselbe Anzahl an Elementen haben. Da gibt es verschiedene Möglichkeiten, aber wir legen uns zunächst eine Variable "linecount" an, die wir später wiederverwenden können, und überprüfen dann, ob unsere Dateien alle dieselbe Anzahl von Zeilen haben. Wenn nicht, werfen wir eine Exception mit einer schicken Fehlermeldung und steigen aus:

Code: Alles auswählen

linecount = len(contents[0])
if not all(len(content) == linecount for content in contents[1:]):
    raise ValueError('Files do not all have the same linecount')
... und ansonsten: weitermachen. Also, wir haben unsere Dateiinhalte jetzt in hübschen Listen, also... sagen wir so: es gibt eine einfache und etwas... unelegante Variante und eine kurze und knackige die ich hier mal beide zeige. Zunächst bauen wir uns aus unseren "contents" aber erstmal eine schicke neue Datenstruktur, die uns das alles deutlich vereinfacht:

Code: Alles auswählen

corresponding_lines = list(zip(*contents))
Dieser Codeabschnitt ist ein bisschen tricky, daher erkläre ich ihn kurz. Also, mit "*contents" machen wir aus unserer "contents"-Variablen eine Parameterliste (ich weiß nicht, wie ich das besser erklären könnte, vielleicht hat einer der geschätzten Mitleser ja eine bessere Idee). Dann verarbeiten wir sie mit der Builtin-Funktion "zip()" und wandeln deren Ergebnis (das ein zip-Objekt, also ein Generator ist) wieder in eine Liste um. Danach sieht unsere Variable "corresponding_lines" so aus -- die jeweils n-ten Zeilen aus jeder Eingabedatei sind jetzt zu einem "tuple()" zusammengefaßt und "corresponding_lines" eine Liste dieser Tupel.

Code: Alles auswählen

corresponding_lines = [('Mark Smith', '555-1234'), ('Bobby Brown', '555-9876'), ...]
Jetzt wollen wir die maximalen Längen jeder n-ten Zeile ermitteln, wie gesagt, das geht in kurz und knackig in einer Zeile mit zwei ineinander verschachtelten List Comprehensions und der Builtin-Funktion "max()":

Code: Alles auswählen

maxlengths = [max([len(string) for string in corresponding_lines[linenumber]]) for linenumber in range(len(corresponding_lines))]
oder in etwas ausführlicher und verständlicher:

Code: Alles auswählen

maxlengths = list()
for linenumber in range(len(corresponding_lines)):
    maxlength = 0
    for string in corresponding_lines[linenumber]:
        length = len(string)
        if length > maxlength:
            maxlength = length
    maxlengths.append(maxlength)
Das Ergebnis in "maxlengths" ist in beiden Fällen dasselbe:

Code: Alles auswählen

maxlengths = [30, 10, 11, 10, 12]
Jetzt wollen wir uns aus diesen Maximallängen mal einen Formatstring zusammenbauen, auch dabei trickse ich wieder ein bisschen:

Code: Alles auswählen

formatstrings = ' '.join(['{:<%ds}'%(maxlength) for maxlength in maxlengths])
Der Trick, den ich mir hier zunutze mache, ist, das Python ganz verschiedene Formatierungsmöglichkeiten hat: die klassische, der C-Funktion "printf" ähnelnde Möglichkeit "<formatstring>%(parameter)", das wie die besagte C-Funktion mit "%<x>"-Platzhaltern arbeitet, wobei hier -- unsere "maxlengths" sind ja Ganzzahlen -- also mit "%d" arbeitet. Und dann gibt es die modernere Version, die mit "<formatstring>.format(parameter)" aufgerufen wird und "{}"-Platzhaltern arbeitet. (Es gibt noch eine ganz moderne Variante namens f-Strings, die der moderneren Variante ähnelt, hier aber mal außen vor bleiben soll. Bitte lies die von mir verlinkten Dokumentationen, das ist wirklich wichtig!)

Also, was haben wir jetzt? Genau: wir haben unsere Dateiinhalte in contents und einen Formatstring, also auf zur Ausgabe:

Code: Alles auswählen

for content in contents:
    print(formatstring.format(*content))
Bitte, bitte, bitte spiel' mit dem Code ein bisschen herum, zieh' die List Comprehensions mal auseinander (siehe dazu mein Beispiel oben mit der langen und der kurzen Variante), und stell' sicher, daß Du meinen Code wirklich, wirklich verstehst -- die weiteren Python-Module werden sicherlich darauf aufbauen. Wenn Du Fragen hast, bitte stell' sie hier im Thread, okay? Zum Abschluß schreibe ich nochmal meinen ganzen Code zusammenhängend und habe dabei allerdings die überflüssige Variable "maxlengths" wegoptimiert, sondern konstruiere mir stattdessen direkt meinen Formatstring mit einem ".join()" auf eine "max()"-Funktion in einer List Comprehension, die auf einer List Comprehension arbeitet, welche wiederum in einer List Comprehension... genau. Viel Glück, Spaß und Erfolg!

Code: Alles auswählen

#!/usr/bin/env python
from argparse import ArgumentParser

if __name__ == '__main__':
    parser = ArgumentParser(description='mimic paste(1)')
    parser.add_argument('filenames', nargs='+', help='the files')
    parser.add_argument('--serial', '-s', action='store_true', help='output as columns')
    args = parser.parse_args()

    contents = list()
    for filename in args.filenames: 
        with open(filename, 'r') as ifh: # diesen abgekürzten Variablennamen mögen manche hier nicht, ich schon ;-)
            content = ifh.read().splitlines() # das entfernt im Gegensatz zu ifh.readlines() auch die Zeilenumbrüche
            contents.append(content)

    # validate input(s)
    linecount = len(contents[0])
    if not all(len(content) == linecount for content in contents[1:]):
        raise ValueError('Files do not all have the same linecount')

    if args.serial:
        # output files as lines
        corresponding_lines = list(zip(*contents))
        formatstring = ' '.join(
            ['{:<%ds}'%(maxlength) for maxlength in [
                max([len(string) for string in corresponding_lines[linenumber]])
                for linenumber in range(len(corresponding_lines))]])
        for content in contents:
            print(formatstring.format(*content))
            
    else:
        # @todo  output files as columns
        pass # this part is left as an excercise to the reader
PS: Oh, ach so... bitte schreib den "@todo"-Teil hinter dem letzten "else:" und zeig ihn uns mal -- dann können wir nämlich sehen, ob Du es halbwegs verstanden hast, und Dir vielleicht noch ein paar Tipps dazu geben. ;-)
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@LukeNukem: Du hast jetzt den normalen Teil gemacht, also das müsste eigentlich im ``else`` stehen wo das @todo steht. Der ``--serial``-Fall gibt eine Datei pro Zeile aus.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

__blackjack__ hat geschrieben: Dienstag 1. Juni 2021, 22:51 @LukeNukem: Du hast jetzt den normalen Teil gemacht, also das müsste eigentlich im ``else`` stehen wo das @todo steht. Der ``--serial``-Fall gibt eine Datei pro Zeile aus.
Also äh... ja, nein, vielleicht... Sorry, unter Linux ist das nicht so. Aus "man 1 paste" (GNU Coreutils):

Code: Alles auswählen

      -s, --serial
              paste one file at a time instead of in parallel
Siehe dazu bitte auch POSIX [1], FreeBSD [2] und Linux [3].

Natürlich kann ich nicht ausschließen, daß Du mit einer anderen UNIX-Implementierung (SCO, Sinix, ...?) arbeitest als ich, aber... ich halte das Verhalten meines Programms auch nach mehreren Tests für weitestgehend korrekt -- diesbezüglich jedenfalls. ;-)

[1] https://pubs.opengroup.org/onlinepubs/0 ... paste.html
[2] https://www.freebsd.org/cgi/man.cgi?query=paste
[3] https://linux.die.net/man/1/paste
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Um die Längen der Dateien zu prüfen, ist der erste Schritt, die Längen zu ermitteln, dann zu schauen, ob sie unterschiedlich sind. Das geht mit einem Set:

Code: Alles auswählen

if len(set(map(len, contents))) > 1:
    raise ValueError('Files do not all have the same linecount')
Über einen Index iteriert man in Python nicht. Bei corresponding_lines gibt es eigentlich auch gar keinen Grund dafür, das ist einfach nur umständlich.

Code: Alles auswählen

        formatstring = ' '.join(
            '{:<%ds}'%(maxlength) for maxlength in [
                max(len(string) for string in lines)
                for lines in corresponding_lines])
Auch die doppelte for-Schleife ist unnötig kompliziert.

Code: Alles auswählen

formatstring = ' '.join('{:<%ds}' % max(map(len, lines)) for lines in zip(*contents))
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@LukeNukem: Okay, das ``list(zip(*contents))`` hatte mich zu der Annahme verleitet, denn das braucht man wenn man den Fall ohne ``-s`` implementiert, aber es ist falsch für den Fall mit ``-s``. Die Aufgabenstellung spricht da explizit nur von ``file1``. Was ich auch komisch finde an der Aufgabenstellung.

Mit dem original ``paste``-Programm hat das auch nichts zu tun, denn dem sind die Längen der Zeilen in beiden Fällen völlig egal, das setzt einfach ein Tabulatorzeichen ohne da irgendwas mit Leerzeichen auszurichten. Oder was anderes als ein Tab wenn man die ``-d``-Option benutzt um etwas anderes anzugeben.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
bahmady
User
Beiträge: 12
Registriert: Sonntag 30. Mai 2021, 15:59

Hallo LukeNukem!

Ich habe letzte Woche die restlichen Aufgaben meiner Hausarbeit fertig gemacht. Unter anderem habe ich ein Problem geschrieben, mit dem man das Cäsar-Chiffre knacken kann.

Um ehrlich zu sein, dein Code ist für mich recht kompliziert. Die Klasse ArgumentParser ist für mich neu. Um die Klasse ein wenig zu verstehen, habe ich mir folgendes Video angeschaut: https://www.youtube.com/watch?v=cdblJqEUDNo&t=32s

Was ich von deinem Code verstehe:

Code: Alles auswählen

# validate input(s)
        linecount = len(contents[0])
        if not all(len(content) == linecount for content in contents[1:]):
            raise ValueError('Files do not all have the same linecount')
Hier soll eine Fehlermeldung auftauchen, falls die beiden Textdateien unterschiedlich lange Zeilen haben.

Code: Alles auswählen

  if args.serial:
            # output files as lines
            corresponding_lines = list(zip(*contents))
            formatstring = ' '.join(
                ['{:<%ds}'%(maxlength) for maxlength in [
                    max([len(string) for string in corresponding_lines[linenumber]])
                    for linenumber in range(len(corresponding_lines))]])
            for content in contents:
                print(formatstring.format(*content))
Hier bildest du den Formatstring. Was ich an deinem Code nicht verstehe ist, wie du contents verwendest und wie du die Bezüge darstellst:

Code: Alles auswählen

 contents = list()
        for filename in args.filenames: 
            with open(filename, 'r') as ifh: # diesen abgekürzten Variablennamen mögen manche hier nicht, ich schon ;-)
                content = ifh.read().splitlines() # das entfernt im Gegensatz zu ifh.readlines() auch die Zeilenumbrüche
                contents.append(content)


Wo ist der Bezug von filename zu meinen beiden Textdateien???

Code: Alles auswählen

texteins = 'C:/Users/bah90/Desktop/t1.txt'
textzwei = 'C:/Users/bah90/Desktop/t2.txt'

Code: Alles auswählen

def paste_s(texteins, textzwei):          
    if __name__ == '__main__':
        parser = ArgumentParser(description='mimic paste(1)')
        parser.add_argument('filenames', nargs='+', help='the files')
        parser.add_argument('--serial', '-s', action='store_true', help='output as columns')
        args = parser.parse_args()

        contents = list()
        for filename in args.filenames: 
            with open(filename, 'r') as ifh: # diesen abgekürzten Variablennamen mögen manche hier nicht, ich schon ;-)
                content = ifh.read().splitlines() # das entfernt im Gegensatz zu ifh.readlines() auch die Zeilenumbrüche
                contents.append(content)
Kannst du mir den Bezug von 'filenames' zu contents erklären? Ich bin übrigens schon für Datenbanksysteme am lernen. Die nächsten Klausurtermine sind erst ab Oktober -.-. Die Klausur ist am 17.07...

LG

bahmady
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

bahmady hat geschrieben: Montag 7. Juni 2021, 15:18 Was ich von deinem Code verstehe:
(...)
Hier soll eine Fehlermeldung auftauchen, falls die beiden Textdateien unterschiedlich lange Zeilen haben.
Korrekt.
bahmady hat geschrieben: Montag 7. Juni 2021, 15:18 (...)
Hier bildest du den Formatstring. Was ich an deinem Code nicht verstehe ist, wie du contents verwendest und wie du die Bezüge darstellst:
Mein Trick ist, daß ich mit der zip()-Funktion zunächst eine Liste von Tupeln baue, und jedes der Tupel-Elemente in der Liste enthält die Inhalte der korrespondierenden Zeilen. Also das Tupel mit dem Index 0 in der Liste enthält die Daten der ersten, das Tupel-Element mit dem Index 1 in der Liste die aus der zweiten Zeile, und so weiter. Dadurch kann ich ganz einfach über eine weitere List Comprehension die Längen der Strings in den Zeilen ermitteln und mit der max()-Funktion die größte Länge dort herausziehen, und mit dieser Länge baue ich dann einen Teil-Formatstring wie "{:<30s}" für einen linksbündig ausgerichteten String mit 30 Zeichen. Diese Teil-Formatstrings werden dann über die str.join()-Methode zum Gesamt-Formatstring zusammengefaßt.

Nun habe ich meine originalen Inhalte aber noch in der Variablen "content", die ebenfalls eine Liste von Listen ist und in jeder der enthaltenen Listen die eingelesenen Inhalte einer meiner Textdateien enthält -- also das, was ich bei '--serial' oder '-s' in einer Zeile ausgeben möchte. Und genau darauf wende ich dann ganz einfach meinen oben zusammengebauten Formatstring an und gebe das Resultat aus.
bahmady hat geschrieben: Montag 7. Juni 2021, 15:18 (...)
Wo ist der Bezug von filename zu meinen beiden Textdateien???
Na klar. Ich habe den ArgumentParser so konfiguriert, daß er sogenannte "positional arguments" entgegennehmen kann, sowas kennst Du vielleicht (hoffentlich) von etlichen Kommandozeilenprogrammen wie less(1), cat(1) oder grep(1). So kannt Du mein Programm im Prinzip mit so vielen Dateinamen aufrufen, wie Du möchtest, bei Dir zum Beispiel mit zwei: "./programm.py names.txt numbers.txt", aber "./programm.py names.txt numbers.txt numbers.txt names.txt" oder andere Dateien gehen natürlich auch. (Es gibt da eine Grenze, die Deine Kommandozeilenumgebung festlegt, aber die ist heutzutage recht groß und soll uns jetzt hier nicht weiter interessieren). Der Name "filenames" und der Parameter "nargs='+'" in "parser.add_argument()" sorgen dafür, daß die übergebenen Dateinamen von parser.parse_args() in einer Liste (hier: "args") abgelegt werden, die Dein Programm dann im Folgenden über das Attribut "args.filenames" lesen, und -- wie ich hier -- darüber iterieren kann. Wenn Du das Programm also mit "./programm.py names.txt numbers.txt" aufrufst, enthält args.filenames nach dem Aufruf von parse_args() die Liste "['names.txt', 'numbers.txt']" -- und wenn ich mit "for filename in args.filenames:" darüber iteriere... genau.
bahmady hat geschrieben: Montag 7. Juni 2021, 15:18 (...)
Kannst du mir den Bezug von 'filenames' zu contents erklären? Ich bin übrigens schon für Datenbanksysteme am lernen. Die nächsten Klausurtermine sind erst ab Oktober -.-. Die Klausur ist am 17.07...
Naja, die "args.filenames" sind wie gerade erklärt die übergebenen Dateinamen, und "filename" enthält dann für jeden Durchlauf der for-Schleife einen der Dateinamen. Ich öffne dann die Datei mit einem sogenannten "Context Manager" (with open()") zum Lesen, lese die Datei komplett ein, splitte den gelesenen Inhalt zeilenweise ("ifh.read().splitlines()") und hänge die so gewonnene Liste von Zeilen an meine Liste "contents" an, so daß "contents" am Ende eine Liste enthält, bei der jedes Element wiederum eine Liste der Zeilen aus der angegebenen Textdatei ist.

Tipp: füg' doch mal' nach dem "corresponding_lines" jeweils ein "print(contents)" und ein "print(corresponding_lines)" ein... viel Spaß und Erfolg! ;-)
bahmady
User
Beiträge: 12
Registriert: Sonntag 30. Mai 2021, 15:59

Nach 31 Beiträgen mehr Fragezeichen als vorher...danke Leute!
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

bahmady hat geschrieben: Freitag 11. Juni 2021, 15:49 Nach 31 Beiträgen mehr Fragezeichen als vorher...danke Leute!
Man kann dir bestimmt helfen. Viele von uns, mich inbegriffen, haben aber anscheinend immer noch nicht ganz verstanden was die genaue Anforderung ist.
Zeig doch nochmal wie die Ausgabe aussehen soll, aber mit [\code] - tags damit die Formatierung nicht verändert wird.
Bisher hattest du auch nur einen Teil der Aufgabe gepostet, daher ist es nicht ganz klar wie das mit dem Rest des Programms zusammenspielen soll.

Es ist wohl auch zielführender, wenn du uns deinen Code zeigst und wir dir Hilfestellung an deinem Code geben.
LukeNukem
User
Beiträge: 232
Registriert: Mittwoch 19. Mai 2021, 03:40

bahmady hat geschrieben: Freitag 11. Juni 2021, 15:49 Nach 31 Beiträgen mehr Fragezeichen als vorher...danke Leute!
Najaaa... Du möchtest oder mußt die Programmiersprache Python lernen. Zum Erlernen einer Programmiersprache ist es meist sehr nützlich, fremden Code zu lesen und zu verstehen -- oder sich jedenfalls ein Verständnis dafür zu erarbeiten. Und zwar, ja, auch Code von Leuten, die mehr Erfahrung haben als man selbst, dabei lernt man gemeinhin am meisten. Und wenn ich Dein Eröffnungsposting richtig verstanden habe, möchtest Du Dir das Verständnis ja auch erarbeiten und keine schlüsselfertige Lösung.

Deswegen, mach's doch einfach so: nimm meinen Code und arbeite ihn Abschnitt für Abschnitt durch. Wenn Du Dich fragst, was in "args.filenames" steht, dann füg' doch einfach mal temporär ein "print('args.filenames:', args.filenames)" oder ein "print('args:', args)" ein. "print()" ist quasi das "Debugging des kleinen Mannes", und auch die Builtin-Funktionen "dir()", "vars()", "type()" und ggf. "getattr()" können dabei sehr nützlich sein, um ein Python-Objekt zu untersuchen. Wenn Du Dein "print()"-Statement eingebaut hast, rufst Du das Programm einfach mal auf und schaust, was in Deinen Variablen steht. Dabei kann es nützlich sein, das Programm nach dem Aufruf von "print()" direkt zu beenden, das ginge mit einem "sys.exit(0)". Wenn Du das Programm einige Male mit verschiedenen Kommandozeilenargumenten aufgerufen und den Zusammenhang zwischen den Argumenten und den Inhalten von "args.filenames" bzw. "args" verstanden hast, nimmst Du das (oder die) "print()"-Aufrufe wieder heraus und wendest Dich dem nächsten Abschnitt des Code zu.

Mein Code benutzt -- bewußt -- viele Comprehensions, also im Prinzip eine Art von syntaktischem Zucker. Dabei ist die List Comprehension

Code: Alles auswählen

dingsbums = [1, 2, 3]
bumsdings = [value**value for value in dingsbums]
quasi so etwas wie eine Kurzform von

Code: Alles auswählen

dingsbums = [1, 2, 3]
bumsdings = list()
for value in dingsbums: 
    bumsdings.append(value**value)
Also probier' mal Folgendes: zieh' meine Comprehensions auseinander, also überführe sie in ihre Langform, und um zu verstehen, was passiert, fügst Du dort -- wie zuvor -- wieder "print()"-Statements ein.

Auf diese Weise arbeitest Du Dich durch den Code und verstehst Stück für Stück das Programm -- und wenn sich dabei Fragen ergeben, dann stell' sie einfach hier. Ja, ich weiß, das ist zeit- und arbeitsaufwändig, aber am Ende, wenn Du alles verstanden hast, wirst Du mit Deinen Python-Kenntnissen ein bedeutendes Stück weiter gekommen sein.

Wie gesagt, ich weiß, das ist aufwändig, aber leider ist es der einzige Weg. Wir können ja schlecht Deinen Kopf aufmachen und unser Wissen hineinkopieren, und wenn wir Dir die einfachste und Deinem aktuellen Niveau angepaßte Lösung vorkauen, hast Du auch nicht viel gelernt. Da können wir Dir helfen und ich glaube, wir haben gezeigt, daß wir das auch gerne tun, aber mehr können wir nicht tun... :-)
bahmady
User
Beiträge: 12
Registriert: Sonntag 30. Mai 2021, 15:59

Hallo zusammen!

Sorry für die späte Antwort. Ich lerne gerade für eine andere Klausur. Nach der Klausurphase mache ich einen Python-Bootcamp, um beim Programmieren besser zu werden. Dies wird jedoch leider erst im September sein -.-

@LukeNukem ich habe die Aufgabe mittlerweile gelöst. Hierzu habe ich deinen Code genommen, den Argparser entfernt und den Code entsprechend modifiziert. Hier mein Code:

Code: Alles auswählen

texteins = 'C:/Users/bah90/Desktop/names.txt'
textzwei = 'C:/Users/bah90/Desktop/numbers.txt'

def paste_s(texteins, textzwei):
    #texteins lesen und splitten
    texteins = open('C:/Users/bah90/Desktop/names.txt', encoding='utf-8') 
    namen = texteins.read()
    namen_split = namen.splitlines(False)
    texteins.close()
    #testzwei lesen und splitten
    textzwei = open('C:/Users/bah90/Desktop/numbers.txt', encoding='utf-8')
    nummern = textzwei.read()
    nummern_split = nummern.splitlines(False)
    textzwei.close()
    #Inhalt beider Dateien zu einer Gesamtliste hinzufügen
    listen = list()
    listen.append(namen_split)
    listen.append(nummern_split)
    #Gesamtliste in zusammengehörige Elemente aufteilen
    zusammengehörige_zeilen = list(zip(*listen))
    #Formatstring rstellen mit der Join-Methode, gekoppelt mit der max() - und der range()-Funktion 
    formatstring = ' '.join(
            ['{:<%ds}'%(maxlength) for maxlength in [
                max([len(string) for string in zusammengehörige_zeilen[linenumber]])
                for linenumber in range(len(zusammengehörige_zeilen))]])
    #Ausgabe der formatierten Liste
    for liste in listen:
        print(formatstring.format(*liste))
Den Argparser und die gesamte Thematik dahinter werde ich versuchen, im Bootcamp nach der Klausur zu erlernen. Der entscheidende Code, den ich die ganze Zeit nicht selbst hingekriegt habe, ist der hier:

Code: Alles auswählen

  formatstring = ' '.join(
            ['{:<%ds}'%(maxlength) for maxlength in [
                max([len(string) for string in zusammengehörige_zeilen[linenumber]])
                for linenumber in range(len(zusammengehörige_zeilen))]])
Ich verstehe hier was die einzelnen Variablen bedeuten. Aber ich habe noch nicht gelernt, wie du all die Variablen mit for und in range() verbunden hast bzw. wie oder wo du die Syntax mit for und in range() gelernt hast. Dazu habe ich keine Schritt für Schritt Erklärung gefunden und wäre für eine Erklärung dankbar.

LG

Belal Ahmady
Antworten