Schach PGN Datei splitten aufteilen

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.
PeterL
User
Beiträge: 95
Registriert: Samstag 6. März 2021, 18:39

Hallo Leute

ich möchte eine PGN Datei in mehrere Dateien aufteilen, und als einzelne Textdatei abspeichern.

Das ist der Inhalt einer PGN Datei. Zuerst stehen dort Informationen die mit der Partie etwas zu tun haben. Danach folgt die Notation, also die Schachzüge. Nach den Schachzügen scheint ein Return als Abschluss Zeichen Gesetz zu sein.
Und genau dort möchte ich die Datei in eine einzelne PGN Datei abspeichern.

In der Datei habe ich dort wo getrennt werden soll etwas geschrieben.



[Event "5th Norway Blitz 2017"]
[Site "Stavanger NOR"]
[Date "2017.06.05"]
[Round "1.1"]
[White "Carlsen,M"]
[Black "Nakamura,Hi"]
[Result "1/2-1/2"]
[WhiteTitle "GM"]
[BlackTitle "GM"]
[WhiteElo "2832"]
[BlackElo "2785"]
[ECO "D00"]
[Opening "Queen's pawn"]
[Variation "Chigorin variation"]
[WhiteFideId "1503014"]
[BlackFideId "2016192"]
[EventDate "2017.06.05"]

1. Nc3 d5 2. d4 Nf6 3. Bf4 a6 4. e3 Bf5 5. Bd3 Bxd3 6. cxd3 e6 7. Nf3 Bd6 8. Bg5
Nbd7 9. O-O h6 10. Bh4 O-O 11. e4 Be7 12. Qb3 Nb6 13. Rac1 c6 14. Rfe1 Rc8 15.
h3 Nfd7 16. Bg3 Re8 17. a3 Bf8 18. Qa2 Nf6 19. b4 Ra8 20. Qb2 Nh5 21. Bh2 Nf6
22. Red1 Qe7 23. Nd2 Rec8 24. Nb3 Nfd7 25. Re1 Qd8 26. Nc5 Ra7 27. a4 Nxc5 28.
dxc5 Nd7 29. exd5 exd5 30. d4 b6 31. b5 axb5 32. axb5 bxc5 33. bxc6 Rxc6 34.
Nxd5 Rg6 35. Qb5 Ra5 36. Qb7 Ra2 37. Bc7 Qa8 38. Qxa8 Rxa8 39. Ne7+ Bxe7 40.
Rxe7 Nf6 41. dxc5 Nd5 42. Rd7 Rc6 43. Rxd5 Rxc7 44. c6 Rac8 45. Rdc5 Kf8 46. Kh2
Ke7 47. R5c3 Kd6 48. Rg3 g5 49. Rf3 Ke7 50. Rfc3 Kd6 51. Kg3 f5 52. h4 Rxc6 53.
Rxc6+ Rxc6 54. Rxc6+ Kxc6 55. hxg5 hxg5 56. f4 g4 57. Kh4 Kd5 58. Kg5 Ke4 59. g3
Kf3 60. Kxf5 Kxg3 61. Ke5 Kh2 62. f5 g3 63. f6 g2 64. f7 g1=Q 65. f8=Q Qg3+ 66.
Qf4 Qxf4+ 67. Kxf4 1/2-1/2
>Hier soll die Datei getrennt werden< Wahrscheinlich ist dort ein Return Zeichen?
[Event "5th Norway Blitz 2017"]
[Site "Stavanger NOR"]
[Date "2017.06.05"]
[Round "1.2"]
[White "Vachier Lagrave,M"]
[Black "Kramnik,V"]
[Result "1-0"]
[WhiteTitle "GM"]
[BlackTitle "GM"]
[WhiteElo "2796"]
[BlackElo "2808"]
[ECO "A07"]
[Opening "Reti"]
[Variation "King's Indian attack (Barcza system)"]
[WhiteFideId "623539"]
[BlackFideId "4101588"]
[EventDate "2017.06.05"]

1. g3 d5 2. Nf3 Nf6 3. Bg2 e6 4. O-O Be7 5. d3 O-O 6. Nbd2 a5 7. c3 a4 8. Qc2 c5
9. e4 Nc6 10. Rd1 b5 11. Nf1 Qb6 12. Bf4 b4 13. Ne5 Bb7 14. Nxc6 Bxc6 15. Ne3 a3
16. cxb4 Qxb4 17. exd5 axb2 18. dxc6 bxa1=Q 19. Rxa1 Qd4 20. Rb1 Bd6 21. Bxd6
Qxd6 22. Rb5 Rfc8 23. Nc4 Qd4 24. Nb6 Nd5 25. Bxd5 exd5 26. Nxa8 h5 27. c7 Qe5
28. Qxc5 h4 29. Qxd5 Qe8 30. gxh4 1-0
nezzcarth
User
Beiträge: 1632
Registriert: Samstag 16. April 2011, 12:47

Was hast du denn schon versucht? Die "saubere" Lösung wäre wohl, sich eine der existierenden Bibliotheken, die PGN lesen und schreiben können (z. B. https://python-chess.readthedocs.io/en/latest/pgn.html) zu suchen und es damit umzusetzen. Solche Formate selbst robust so zu verarbeiten, dass das mit Dateien aus beliebigen Quellen gut funktioniert, ist meist doch aufwendiger, als es im ersten Moment erscheinen mag.
PeterL
User
Beiträge: 95
Registriert: Samstag 6. März 2021, 18:39

Hallo,

die Schach Bibliotheken sind nicht das was ich suche.
Ich möchte eigentlich zeilenweise eine PGN Datei einlesen bis zum Return Zeichen. Dann möchte ich die Datei in eine einzelne Datei abspeichern. Es ist auch egal ob es eine PGN Datei ist, es kann auch eine normale Textdatei sein. Ich weis leider nicht wie ich das Return Zeichen finden kann, wenn ich die Datei Zeilenweise einlese. Das ist das Problem.
Eine Datei zeilenweise einlesen krieg ich noch hin. Wie frage ich dann das Return Zeichen im Code ab ?

Bis dann...
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Du willst also wissen, wie Du eine leere Zeile erkennen kannst? line == "\n"?
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@PeterL,

ich glaube du hast @nezzcarth falsch verstanden. Die vorgeschlagene Bibliothek hat neben vieler andere Funktionen, nämlich auch die, eine Schach-PGN Datei zu lesen. Hast du dir den Link wenigstens mal angeschaut? Für mich sieht das genau nach der Funktion aus die du suchst.
PeterL
User
Beiträge: 95
Registriert: Samstag 6. März 2021, 18:39

Hallo,

angesehen habe ich mir den Link, ich habe leider nichts verstanden.
Es wird ein Beispiel genannt nach dem aber der Partienamen bekannt sein muss.
Und die Partienamen weis ich eben nicht, da es sich um Tausende Partien handelt.

Zeilenweise einlesen und das Return erkennen halte ich für machbar.
Ich bin noch Anfänger und möchte schnelle erfolge haben.

Ich werde dem nächst Pycharm installieren. Und dann kann ich mich irgendwann mit dem Schach link beschäftigen.

Es kommt mir beim Splitten der Partien nicht auf die Geschwindigkeit an.

Bis die Tage . Danke für euere Hilfe...
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

PeterL hat geschrieben: Freitag 24. September 2021, 09:56 Ich bin noch Anfänger und möchte schnelle erfolge haben.
Hallo PeterL,
das könnte man so interpretieren, dass du nicht deine eigene Zeit investieren willst, zu erlernen, wie man das machen könnte, sondern andere es dir vorkauen sollen.

Nun, ich ignoriere das mal und dieser, auch für Anfänger geeignete Code, macht das was du willst.

Code: Alles auswählen

with open('datei.pgn', 'r') as infile:
    counter = 1
    eof_infile = False
    while not eof_infile:
        filename = f'spiel-{counter:04d}.pgn'
        print(filename)
        with open(filename, 'w') as outfile:
            cr_counter = 0
            while cr_counter < 2:
                line = infile.readline()
                if len(line) == 0:
                    eof_infile = True
                    break
                if line == '\n':
                    cr_counter += 1
                if cr_counter < 2:
                    outfile.write(line)
        counter += 1
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Streng genommen müsste man sich an die formale Syntax halten und ich nehme an, dass dies bei der python-chess Library auch so ist:

Code: Alles auswählen

<PGN-database> ::= <PGN-game> <PGN-database>
                   <empty>

<PGN-game> ::= <tag-section> <movetext-section>

<tag-section> ::= <tag-pair> <tag-section>
                  <empty>

<tag-pair> ::= [ <tag-name> <tag-value> ]

<tag-name> ::= <identifier>

<tag-value> ::= <string>

<movetext-section> ::= <element-sequence> <game-termination>

<element-sequence> ::= <element> <element-sequence>
                       <recursive-variation> <element-sequence>
                       <empty>

<element> ::= <move-number-indication>
              <SAN-move>
              <numeric-annotation-glyph>

<recursive-variation> ::= ( <element-sequence> )

<game-termination> ::= 1-0
                       0-1
                       1/2-1/2
                       *
<empty> ::=
Das wäre also der saubere Weg.

Hier noch ein anderer quick & dirty Ansatz: sobald ein tag-pair erkannt wird, aber die vorhergehende Zeile kein tag-pair enthielt, so handelt es sich um eine neue Partie. Ungetestet:

Code: Alles auswählen

last_leading_char = ""
tag_start_char = "["
counter = 0
with open('datei.pgn', 'r') as pgn_file:
    for line in pgn_file:
        leading_char = line[0]
        if leading_char == tag_start_char and last_leading_char != tag_start_char:
            try:
                outfile.close()
            except NameError:
                pass
            counter += 1
            outfile = open(f"game-{counter}.pgn", "w")
        last_leading_char = leading_char
        outfile.write(line)
    outfile.close()


Anfänger seien gewarnt vor quick & dirty Ansätzen :)
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@PeterL,
Es wird ein Beispiel genannt nach dem aber der Partienamen bekannt sein muss.
Und die Partienamen weis ich eben nicht, da es sich um Tausende Partien handelt.
Die Partiennamen werden dir mit python-chess komfortabel aus der Datei extrahiert. Die musst du nicht vorher wissen. Das einzige was du wissen must, ist der Dateiname deiner PGN-Datei.

Code: Alles auswählen

import chess.pgn
from itertools import count
from pathlib import Path
import re


def main():
    with open("chess_events.pgn", "r") as pgn_read_file:
        for num in count():
            game = chess.pgn.read_game(pgn_read_file)
            if not game:
                break
            file_name = game.headers["Event"].lower()
            file_name = re.sub(r'[\\/*?:"<>|\s]',"",file_name)

            pgn_file = Path("games", f"{num:04d}_{file_name}").with_suffix(".pgn")
            pgn_file.write_text(str(game))


if __name__ == "__main__":
    main()
Bei mir enthält die Datei "chess_events.pgn" über 3000 Partien.
Mit diesen wenigen Zeilen werden sie alle in einzelne PGN Dateien abgespeichert und zwar mit dem Partienamen im Dateinamen.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Alternative zu `itertools.count()` und dem lesen/abbrechen in der ``for``-Schleife:

Code: Alles auswählen

        games = iter(lambda: chess.pgn.read_game(pgn_file), None)
        for num, game in enumerate(games):
            ...
Das man `iter()` auf diese Weise mit einem zweiten Argument verwenden kann vergesse ich selbst ständig. 😎
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

Ooh Mann! Ich bin so aufgeregt, :idea: 👏👏, mein erster Usecase für den Walruss Operator:

Code: Alles auswählen

import re
from pathlib import Path
from itertools import count
import chess.pgn


def main():
    counter = count()
    with open("chess_events.pgn", "r") as pgn_reader:
        while game := chess.pgn.read_game(pgn_reader):
            file_name = game.headers["Event"].lower()
            file_name = re.sub(r'[\\/*?:"<>|\s]', "", file_name)

            pgn_file = Path("games", f"{next(counter):04d}_{file_name}").with_suffix(".pgn")
            pgn_file.write_text(str(game))


if __name__ == "__main__":
    main()
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Jetzt weiß ich endlich, wofür der Walrus Operator gut ist: man kann aus einer for-Schleife eine while-Schleife machen und alle User mit Python < 3.8 wundern sich ... 8)
PeterL
User
Beiträge: 95
Registriert: Samstag 6. März 2021, 18:39

Hallo Leute, ich konnte die Beispiele gut gebrauchen. Derzeit schreibe ich an einem eigenen Scrip. Ich bin da auf ein Problem gestoßen.
Ich lese die PGN Datei Zeilenweise ein. Ich möchte die Zeilen nicht sofort speichern,
sondern in einer Variable ablegen. Erst wenn die gesamte Datei in der Variable sich befindet , möchte ich diese dann in einem Rutsch auf die Festplatte speichern.
Wenn ich derzeit versuche die Variable mit Print auszugeben wird mir alles so ausgegeben.

['[Event "8th RUS-CHN Summit Men Classical"]\n', '[Event "8th RUS-CHN Summit Men Classical"]\n', '[Event "8th RUS-CHN Summit Men Classical"]\n', '[Event "8th RUS-CHN Summit Men Classical"]\n', '[Event "8th RUS-CHN Summit Men Classical"]\n', '[Event "8th RUS-CHN Summit Women Classical"]\n', '[Event "8th RUS-CHN Summit Women Classical"]\n', '

und so sollte es auf der Console und in einer Textdatei aussehen.

['[Event "8th RUS-CHN Summit Men Classical"]
[Event "8th RUS-CHN Summit Women Classical"]
[Event "8th RUS-CHN Summit Women Classical"]

Hat jemand eine Lösung für mich.

Danke..
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Willst Du bestimmt nicht. Das was Du da siehst, ist die Stringrepräsentation einer Liste, und die ist nur für Debuggingzwecke gedacht, wenn Du etwas ausgeben willst, muß Du die Einträge der Liste einzeln ausgeben, oder eben richtig in eine Datei speichern, z.B. per file.writelines.
PeterL
User
Beiträge: 95
Registriert: Samstag 6. März 2021, 18:39

Hallo, ich glaube ich habe es hinbekommen. Nachdem open lege ich eine Liste an.

daten=[] #Eine Liste erstellen

daten = [line.strip()] # Hier wird alles aus der Textdatei ohne
#Zeilenumbruch zwischen den einzelnen Einträgen gespeichert

for zahl in daten: #Und hier wird die Liste ausgelesen.
print(zahl) #Ausgabe auf der Console

Ergebnis:
[Event "40th TCh-GRE 2012"]
[Event "40th TCh-GRE 2012"]
[Event "40th TCh-GRE 2012"]
[Event "40th TCh-GRE 2012"]

Bis dann...
PeterL
User
Beiträge: 95
Registriert: Samstag 6. März 2021, 18:39

Hallo Leute,

ich habe ein Problem mit einer Liste.
Ich öffne eine pgn Textdatei und lese Zeilenweise die Inhalte ein.
Ich möchte aber nur bestimmte Einträge in einer Liste Speichern und diese Dann abspeichern.

Ich habe schon alles mögliche versucht. Es wird mir immer nur der Letzte Eintrag ausgegeben, aber nicht die gesamte Liste
Wo liegt der Fehler ?

with open('pgn/game-8.pgn', 'r') as pgn_file:
daten=[]#liste erstellen
for line in pgn_file:

if line.find ('[Event "') == 0 and line.count('[Event "')==1 and line.find('"]') > 0 and line.count ('"]') ==1:

daten = [line.strip()] #in Liste ablegen

elif line.find ('[Site "') == 0 and line.count('[Site "')==1 and line.find('"]') > 0 and line.count ('"]') ==1:
daten = [line.strip()]# in Liste ablegen

for zahl in daten:
print(zahl)

Hier wird mit nur der Letzte Eintrag ausgegeben
[Site "St Petersburg RUS"]

So sollte es aussehen
[Event "8th RUS-CHN Summit Women Classical"]
[Site "St Petersburg RUS"]

Bis die Tage...
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@PeterL: ``daten = [line.strip()]# in Liste ablegen`` bindet `daten` an eine Liste mit genau einem Element. Was immer *vorher* an den Namen `daten` gebunden war, ist damit natürlich nicht mehr an den Namen gebunden. Du willst an der Stelle nicht immer neue Listen mit jeweils einem Element erstellen, sondern das Element an eine bereits vorhandene, vor der Schleife erstellte, leere Liste *anhängen*. Diese leere Liste erstellst Du ja sogar schon.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
PeterL
User
Beiträge: 95
Registriert: Samstag 6. März 2021, 18:39

Du willst an der Stelle nicht immer neue Listen mit jeweils einem Element erstellen, sondern das Element an eine bereits vorhandene, vor der Schleife erstellte, leere Liste *anhängen*. Diese leere Liste erstellst Du ja sogar schon.
---

Hallo ,ich verstehe nicht was du meinst.
Es wird immer nur ein Listeneintrag ausgegeben, ich möchte gerne alles ausgeben,
die gesamte Liste.

Wie wird das programmiert ?

Bis die Tage
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Hi PeterL,

diese Zeilen mit

Code: Alles auswählen

daten = [line.strip()] #in Liste ablegen
machen nicht das was du denkst.

Du erstellst da eine neue Liste mit einem Element.
Du möchtest dieses Element an die existierende Liste anhängen. Google doch mal was "anhängen" auf Englisch heißt und dann "Python anhängen an eine Liste".
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
PeterL
User
Beiträge: 95
Registriert: Samstag 6. März 2021, 18:39

Hallo, also jetzt scheint es zu laufen. Leider wird mir die Liste mit Zeilenumbruch ausgegeben.

daten.append(line)

Ergebniss:
[Event "8th RUS-CHN Summit Women Classical"]

[Site "St Petersburg RUS"]

[Date "2012.07.02"]

und
so möchte ich es ausgegeben haben

[Event "8th RUS-CHN Summit Women Classical"]
[Site "St Petersburg RUS"]
[Date "2012.07.02"]

daten.append = [line.strip()] gibt immer eine Fehlermeldung aus.
daten.append[line.strip()]
daten.append [line.strip()]

Bis die Tage
Antworten