14 Units für den elektronischen Datenaustausch in Handel, Logistik, Seefracht und Finanzwesen — vom Preiskatalog über Bestellung, Lieferschein und Rechnung bis zur Zoll- und Containermeldung. Alles baut auf std.edi.core auf; kein externes EDIFACT-Framework, kein fremder Parser.
→ Übersicht · Standard Library · Guide: EDI in der Praxis · Welche Unit?
Alle Beispiele dieser Seite sind mit lyxc 1.0.21A übersetzt und ausgeführt; die gezeigten Ausgaben sind echte Programmausgaben.
UN/EDIFACT ist der internationale Standard für den Datenaustausch zwischen Unternehmen. Eine Nachricht ist reiner Text aus Segmenten — ein Drei-Buchstaben-Tag, dann Datenelemente, abgeschlossen mit dem Segmentzeichen:
UNA:+.? 'UNB+UNOA:1+4012345000009+4098765000004+20260813:0930+IC000042'UNH+1+ORDERS:D:01B:UN'...'UNT+2+1'UNZ+1+IC000042'
Die Hierarchie ist dreistufig — Umschlag, optionale Gruppe, Nachricht:
| Ebene | Öffnet | Schließt | Inhalt |
|---|---|---|---|
| Interchange (Umschlag) | UNB | UNZ | Absender, Empfänger, Datum, Interchange-Referenz |
| Gruppe (optional) | UNG | UNE | fasst gleichartige Nachrichten zusammen |
| Nachricht | UNH | UNT | eine Geschäftsnachricht, z. B. ORDERS |
Vorangestellt werden darf ein UNA-Segment: neun Bytes, die die Trennzeichen bekanntgeben. Fehlt es, gelten die UNOA-Standardwerte.
| Rolle | Zeichen | Code | Konstante |
|---|---|---|---|
| Komponententrenner | : | 58 | EDI_DEF_COMP |
| Datenelementtrenner | + | 43 | EDI_DEF_DATA |
| Dezimalzeichen | . | 46 | EDI_DEF_DEC |
| Escape (Release) | ? | 63 | EDI_DEF_REL |
| Segmentende | ' | 39 | EDI_DEF_SEG |
Zeichensätze: EDI_CHARSET_UNOA (A–Z, 0–9, wenige Sonderzeichen — Vorgabe), EDI_CHARSET_UNOB (ISO-8859-1), EDI_CHARSET_UNOC (UTF-8).
| Unit | Nachrichten | Beschreibung |
|---|---|---|
| std.edi.core | UNA/UNB/UNZ/UNG/UNE/UNH/UNT | Umschlag, Segment-Tokenizer, Escape-Behandlung, Sequenznummern, Duplikatserkennung |
| std.edi.messages | CONTRL, APERAK, GENRAL | Quittung, Fehlermeldung, freie Nachricht |
| std.edi.catalog | PRICAT, PARTIN | Preiskatalog, Partnerstammdaten; GTIN-/GLN-Prüfziffern |
| std.edi.partner | AS2/SFTP-Profile | Partner-Registry mit Nachrichtentyp-Bitmaske |
| std.edi.rfq | REQOTE, QUOTES | Preisanfrage und Angebot |
| std.edi.orders | ORDERS, ORDRSP, ORDCHG | Bestellung, Auftragsbestätigung, Bestelländerung |
| std.edi.invoice | INVOIC, INVCON, COMDIS, COACSU | Rechnung, Rechnungsbestätigung, Disput, Saldenabstimmung |
| std.edi.shipping | DESADV, RECADV | Lieferschein mit SSCC-Hierarchie, Wareneingang |
| std.edi.payment | REMADV, PAYMUL, DIRDEB u. a. (16 Typen) | Zahlungsverkehr; IBAN-/BIC-Prüfung |
| std.edi.inventory | INVRPT, SLSRPT, SLSFCT | Lagerbestand, Absatzbericht, Absatzprognose |
| std.edi.delfor | DELFOR, DELJIT | Lieferabruf und JIT-Feinabruf |
| std.edi.customs | CUSCAR, CUSDEC, CUSEXP, CUSRSP | Zoll: Frachtanmeldung, Zollanmeldung, Behördenantwort |
| std.edi.seafreight | BAPLIE, MOVINS, COPARN, CODECO, COARRI, COPINO, COPRAR, VERMAS, HANMOV | Seefracht: Stauplan, Containermeldungen, VGM (SOLAS) |
| std.edi.transport | IFTMIN, IFTMBC, IFTMCS, IFTMAN, IFCSUM, IFTSTA, IFTFCC, IFTDGN | Multimodaler Transport: Auftrag, Status, Gefahrgut |
Jede Fachunit zieht std.edi.core mit — import std.edi.orders; genügt.
…_PTR bzw. …_GTIN) und Länge (…LEN); der Puffer muss leben, solange die Struct benutzt wird.EDI_*_SIZE exportiert. Der Aufrufer legt an, nullt und gibt frei.int64 × 100: 1999 = 19,99 €, 1900 = 19,00 % Steuer. Kein f64 im Zahlungsverkehr.std.net; std.edi.partner verwaltet nur die Verbindungsparameter.
import std.edi.core;
import std.alloc;
import std.string;
// Zero-Copy-Felder sind nicht nullterminiert: terminierte Kopie fuer die Ausgabe.
fn Feld(ptr: int64, len: int64): pchar {
var b: int64 := alloc(len + 1);
var i: int64 := 0;
while (i < len) { poke8(b + i, peek8(ptr + i)); i := i + 1; }
poke8(b + len, 0);
return b as pchar;
}
fn main(): int64 {
var ctx: int64 := alloc(EDI_CTX_SIZE);
EdiCtxInit(ctx);
poke64(ctx + EDI_CTX_SENDER, "4012345000009"c as int64);
poke64(ctx + EDI_CTX_SNDLEN, 13);
poke64(ctx + EDI_CTX_RCVR, "4098765000004"c as int64);
poke64(ctx + EDI_CTX_RCVLEN, 13);
poke64(ctx + EDI_CTX_ICREF, "IC000042"c as int64);
poke64(ctx + EDI_CTX_ICREFLEN, 8);
poke64(ctx + EDI_CTX_DATE, "20260813"c as int64);
poke64(ctx + EDI_CTX_TIME, "0930"c as int64);
var out: int64 := alloc(4096);
var pos: int64 := 0;
pos += EdiUnaWrite(ctx, out + pos, 4096 - pos);
pos += EdiUnbWrite(ctx, out + pos, 4096 - pos);
pos += EdiUnhWrite(ctx, "1"c, "ORDERS"c, out + pos, 4096 - pos);
// ... hier stehen die Fachsegmente, z. B. aus EdiOrdersWrite ...
pos += EdiUntWrite(ctx, 2, "1"c, out + pos, 4096 - pos);
pos += EdiUnzWrite(ctx, 1, out + pos, 4096 - pos);
PrintLn(Feld(out, pos));
// Wieder einlesen: UNA gibt die Trennzeichen vor
var una: int64 := alloc(EDI_UNA_SIZE);
var start: int64 := 0;
if (EdiUnaRead(out, pos, una) == EDI_OK) { start := 9; }
var sctx: int64 := alloc(EDI_SEGCTX_SIZE);
EdiSegCtxInit(sctx, una, start);
var seg: int64 := alloc(EDI_SEG_SIZE);
// EdiUnbRead erwartet GENAU ein Segment - erst mit dem Iterator holen.
var ctx2: int64 := alloc(EDI_CTX_SIZE);
EdiCtxInit(ctx2);
if (EdiSegmentNext(out, pos, sctx, seg) == 1) {
EdiUnbRead(peek64(seg + EDI_SEG_PTR), peek64(seg + EDI_SEG_LEN), ctx2);
}
PrintLn(StrConcat("Absender: ", Feld(peek64(ctx2 + EDI_CTX_SENDER), peek64(ctx2 + EDI_CTX_SNDLEN))));
PrintLn(StrConcat("ICR: ", Feld(peek64(ctx2 + EDI_CTX_ICREF), peek64(ctx2 + EDI_CTX_ICREFLEN))));
var n: int64 := 1;
while (EdiSegmentNext(out, pos, sctx, seg) == 1) {
n += 1;
PrintLn(StrConcat(" Segment: ", Feld(peek64(seg + EDI_SEG_PTR), 3)));
}
PrintLn(StrConcat("Segmente: ", IntToStr(n)));
return 0;
}
UNA:+.? 'UNB+UNOA:1+4012345000009+4098765000004+20260813:0930+IC000042'UNH+1+ORDERS:D:01B:UN'UNT+2+1'UNZ+1+IC000042'
Absender: 4012345000009
ICR: IC000042
Segment: UNH
Segment: UNT
Segment: UNZ
Segmente: 4
Die Fachunits erzeugen nur den Nachrichtenrumpf (BGM, DTM, NAD, LIN …). Der Umschlag kommt immer aus std.edi.core: UNA + UNB + UNH davor, UNT + UNZ dahinter.
import std.edi.catalog;
import std.edi.shipping;
import std.edi.payment;
import std.edi.seafreight;
import std.edi.transport;
import std.string;
import std.alloc;
fn J(v: int64): pchar { if (v == 1) { return "gueltig"; } return "ungueltig"; }
fn main(): int64 {
PrintLn(StrConcat("GTIN-13 4012345678901: ", J(EdiGtinCheck("4012345678901"c, 13))));
PrintLn(StrConcat("GLN 4012345000009: ", J(EdiGlnCheck("4012345000009"c, 13))));
var sscc: int64 := alloc(19);
EdiSsccGenerate("34012345000000001"c, sscc);
PrintLn(StrConcat(StrConcat("SSCC-18 ", sscc as pchar), StrConcat(": ", J(EdiSsccCheck(sscc as pchar, 18)))));
PrintLn(StrConcat("IBAN DE89370400440532013000: ", J(EdiIbanCheck("DE89370400440532013000"c, 22))));
PrintLn(StrConcat("BIC COBADEFFXXX: ", J(EdiBicCheck("COBADEFFXXX"c, 11))));
PrintLn(StrConcat("Container MSKU1234565: ", J(EdiContainerCheck("MSKU1234565"c, 11))));
PrintLn(StrConcat("UN-Nummer 1203: ", J(EdiUnNumberCheck("1203"c, 4))));
return 0;
}
GTIN-13 4012345678901: gueltig
GLN 4012345000009: gueltig
SSCC-18 340123450000000017: gueltig
IBAN DE89370400440532013000: gueltig
BIC COBADEFFXXX: gueltig
Container MSKU1234565: gueltig
UN-Nummer 1203: gueltig
| Prüfung | Unit | Länge | Verfahren |
|---|---|---|---|
EdiGtinCheck | catalog | 8, 12, 13 oder 14 | GS1 Mod-10 |
EdiGlnCheck | catalog | genau 13 | GS1 Mod-10 |
EdiSsccCheck / EdiSsccGenerate | shipping | 18 (Generator: 17 → 18) | GS1 Mod-10 |
EdiIbanCheck | payment | ≥ 4 | ISO 13616, MOD-97 |
EdiBicCheck | payment | 8 oder 11 | Formatprüfung ISO 9362 |
EdiContainerCheck | seafreight | 11 | ISO 6346 |
EdiUnNumberCheck | transport | genau 4 | UN-Gefahrgutnummer |
Alle liefern 1 = gültig, 0 = ungültig. Keine Write-Funktion prüft von sich aus — Prüfziffern sind Sache des Aufrufers.
| Konstante | Wert | Bedeutung |
|---|---|---|
EDI_OK | 0 | erfolgreich |
EDI_ERR_TRUNC | 1 | Nachricht/Puffer abgeschnitten |
EDI_ERR_NOUNA | 2 | kein UNA-Segment — Standardtrennzeichen gelten |
EDI_ERR_SYNTAX | 3 | Segment nicht lesbar |
EDI_ERR_DUP | 4 | Interchange-Referenz schon gesehen |
EDI_ERR_OVERFLOW | 5 | Zielpuffer zu klein / Partner-Slots voll |
EDI_ERR_PARTNER | 6 | Partner nicht gefunden |
EDI_ERR_NOUNA ist kein Fehler im engeren Sinn: fehlt UNA, füllt EdiUnaRead die Standardtrennzeichen ein und die Verarbeitung läuft weiter — nur der Startversatz ist dann 0 statt 9.
Die …Write-Funktionen melden Überlauf mit -1; ihr Rückgabewert ist sonst die Zahl geschriebener Bytes.
Beschaffung
Käufer Lieferant
REQOTE (Anfrage) →
← QUOTES (Angebot)
ORDERS (Bestellung) →
← ORDRSP (Bestaetigung)
ORDCHG (Aenderung) →
← DESADV (Lieferavis, SSCC)
RECADV (Wareneingang) →
← INVOIC (Rechnung)
REMADV (Zahlungsavis) →
Seefracht-Container
Verlader Terminal / Carrier
COPARN (Anmeldung) →
← CODECO (Gate-In / Gate-Out)
VERMAS (VGM, SOLAS) →
← BAPLIE (Stauplan)
← COARRI (Ankunft)
← COPINO (Abholfreigabe)
Quittungen — auf jede eingehende Nachricht antwortet CONTRL (Syntaxquittung) oder APERAK (fachliche Fehlermeldung), siehe std.edi.messages.
EdiUnbRead will genau ein Segment. Wer den ganzen Interchange übergibt, bekommt zu lange Felder zurück (die Interchange-Referenz enthält dann das nächste Segment). Erst EdiSegmentNext, dann EdiUnbRead mit EDI_SEG_PTR/EDI_SEG_LEN.msgRef und msgType sind Zeiger, keine Zahlen. EdiUnhWrite(ctx, „1“c, „ORDERS“c, …) — eine echte Zahl an dieser Stelle führt zum Absturz, weil die Funktion sie als Adresse liest. Dasselbe gilt für msgRef in EdiUntWrite und grpRef/msgType in EdiUngWrite.EdiUnaRead sagt, welcher Fall vorliegt.MOA+77:776.76 kommt als 77676 zurück. Wer den Wert direkt anzeigt, zeigt das Hundertfache.Weiterführend: Guide: EDI in der Praxis · std.edi.core · std.net · Rohspeicher
Alle Units liegen unter std/edi/: core.lyx, messages.lyx, catalog.lyx, partner.lyx, rfq.lyx, orders.lyx, invoice.lyx, shipping.lyx, payment.lyx, inventory.lyx, delfor.lyx, customs.lyx, seafreight.lyx, transport.lyx.
Letzte Aktualisierung: 2026-08-13 — Trennzeichen-Tabelle repariert (war eine einspaltige Liste ohne Kopf), Schnellstart durch ein lauffähiges Programm ersetzt (die alten Aufrufe hatten falsche Signaturen), Umschlag-Hierarchie, Prüfziffer-Übersicht, Fehlercodes und Fallstricke ergänzt. Geprüft gegen std/edi/*.lyx und lyxc 1.0.17K.
Codebeispiele geprüft: gegen lyxc 1.2.5C übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die pub fn-Signaturen in aurum/std gehalten, 0 Abweichungen).