How to Batch Import and Export PGN
This guide shows how to ingest multi-game PGN streams directly into a libscid database and export stored games back into formatted PGN text using the C ABI.
1. Overview of Symbols
scid_database_import_pgn: Ingests a raw PGN buffer containing one or more games, appending them into the database collection.scid_database_game_export_pgn: Serialises an individual game record identified by database index into PGN format.scid_game_pgn_options: Optional formatting configuration handle for exports.
2. Complete Recipe
#include <scid/scid.h>
#include <stdio.h>
#include <string.h>
static int
check(
scid_error error,
const char* call)
{
if (error == SCID_OK)
{
return 1;
}
fprintf(stderr, "%s failed with scid_error %hu\n", call, error);
return 0;
}
static int
contains(
const char* text,
const char* expected)
{
return strstr(text, expected) != NULL;
}
int
main(void)
{
const char* pgn = "[Event \"Database PGN\"]\n"
"[Site \"Toronto\"]\n"
"[Date \"2026.06.14\"]\n"
"[White \"Alpha\"]\n"
"[Black \"Beta\"]\n"
"[Result \"1-0\"]\n"
"\n"
"1. e4 e5 2. Nf3 1-0\n"
"\n"
"[Event \"Database PGN Two\"]\n"
"[Site \"Vancouver\"]\n"
"[Date \"2026.06.15\"]\n"
"[White \"Gamma\"]\n"
"[Black \"Delta\"]\n"
"[Result \"0-1\"]\n"
"\n"
"1. d4 Nf6 0-1\n";
scid_database* database = NULL;
char diagnostic[1024];
char exported[2048];
char event[128];
size_t count = 0;
size_t diagnostic_size = 0;
size_t exported_size = 0;
size_t event_size = 0;
size_t imported_count = 0;
if (!check(scid_database_create_memory("pgn-io", &database), "scid_database_create_memory") ||
!check(
scid_database_import_pgn(
database, pgn, strlen(pgn), diagnostic, sizeof(diagnostic), &diagnostic_size,
&imported_count),
"scid_database_import_pgn") ||
diagnostic_size != 0 || imported_count != 2 ||
!check(scid_database_game_count_get(database, &count), "scid_database_game_count_get") ||
count != 2 ||
!check(
scid_database_game_tag_get(database, 0, "Event", event, sizeof(event), &event_size),
"scid_database_game_tag_get") ||
!check(
scid_database_game_export_pgn(database, 0, exported, sizeof(exported), &exported_size),
"scid_database_game_export_pgn") ||
!contains(exported, "[Event \"Database PGN\"]") || !contains(exported, "1-0"))
{
fprintf(stderr, "%.*s\n", (int)diagnostic_size, diagnostic);
scid_database_free(database);
return 1;
}
printf("imported games: %zu\n", imported_count);
printf("first event: %.*s\n", (int)event_size, event);
printf("exported bytes: %zu\n", exported_size);
scid_database_free(database);
return 0;
}
3. Key Concepts and Patterns
Streaming PGN Ingestion
Rather than manually parsing individual games in application code, scid_database_import_pgn consumes multi-game PGN buffers directly:
const char* multi_game_pgn = "...";
size_t imported_count = 0;
char diagnostic[1024];
size_t diagnostic_size = 0;
scid_database_import_pgn(
database,
multi_game_pgn,
strlen(multi_game_pgn),
&imported_count,
diagnostic,
sizeof(diagnostic),
&diagnostic_size);
printf("Imported %zu games\n", imported_count);