Forum: PC-Programmierung Byte Order Mark


von Lothar (Gast)


Lesenswert?

Bin grade bei der Portierung von C Code von Unix auf Win 10 auf den mir 
bis dahin völlig unbekannten Byte Order Mark gestossen:

https://de.wikipedia.org/wiki/Byte_Order_Mark

Bei einer Datei mit einer Zahl zum Einlesen drin, die im Notepad noch 
normal aussieht, zeigt der HEX-Editor am Anfang extra:

EF BB BF

Das soll die UTF-8 Kennung sein.

Ich habe das Problem jetzt erst mal mit dem Holzhammer gelöst, damit es 
auf Unix und Win 10 läuft. Geht es noch anders?

while (true)
{
  int num = fgetc(fp);
  if (num == EOF)
  {
    str[idx] = 0;
    break;
  }
  if (('0' <= num) && (num <= '9'))
    str[idx++] = (char) num;
}

von FOp (Gast)


Lesenswert?

Wenn in der Datei eh nur Ziffern drin sein sollen, geht das so.
Bei denen unterscheiden sich die Binärdaten nicht, egal ob ASCII 
codiert, MS Windows mit Codepage 1252 codiert, ISO 8859-1 codiert, ISO 
8859-15 codiert, MS DOS mit Codepage 437 oder 850 codiert, UTF8 auf 
einem System mit Little Endian oder UTF8 auf einem System mit Big Endian 
codiert.

Die Byte Order Mark oder kurz nur BOM ist dafür gedacht, bei UTF8 zu 
erkennen, in welcher Reihenfolge 16-bittige Codes abgelegt wurden. 
Nebenbei hilft sie natürlich auch UTF8 schnell und einfach zu erkennen. 
Aber da die Codes für Ziffern alle 8-bittig sind, kann Dir das egal 
sein.

Mit Deinem Code würdest Du sogar Tausendertrennzeichen oder Leerzeichen, 
die jemand zur besseren Lesbarkeit für Menschen eingefügt hat abkönnen.

Sollte natürlich mal die Datei zerschossen sein, bastelt der Code halt 
eine Zahl draus ohne jegliche Warnung.

von Tilo R. (joey5337) Benutzerseite


Lesenswert?

Kommt halt drauf an, was sonst noch in der Datei steht das du auslesen 
möchtest und welche Mittel dir im Code zur Verfügung stehen.

Die nächst-aufwendigere Stufe wäre imho eine Regex. Damit lassen sich 
auch Kommentare o.ä. leicht in das Dateiformat integrieren.

Noch aufwendiger wäre ein richtiger Parser mit Grammatik. Es würde mich 
aber wundern, wenn sich das lohnt.

von MaWin (Gast)


Lesenswert?

Lothar schrieb:
> Geht es noch anders?

Sicher.

Der elegante Weg ist ein eigenes fgetc.
Das liest ein ASCII Zeichen, wandelt die mit gesetztem high bit vom 
lokalen Zeichensatz in Unicode, und gibt es zurück.
Wenn jedoch am Dateianfang (ftell(f)==0) eine BOM stand, ändert es 
seinen Lesemodus und sammelt es UTF8 Zeichen bis das Unicode-Zeichen 
komplett ist und liefert dann das.
Stand am Dateianfang ein UTF16 oder UTF32 BOM liest es jene in passender 
Byteorder.
Dabei muss es nicht nur -1 für Dateiende sondern auch 0xFFFD liefern 
wenn eine ungültige Zeichenfolge gefunden wurde

So kannst du jede Datei korrekt lesen. Die eine Funktion taugt für 
immer.

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

FOp schrieb:
> Die Byte Order Mark oder kurz nur BOM ist dafür gedacht, bei UTF8 zu
> erkennen, in welcher Reihenfolge 16-bittige Codes abgelegt wurden.
> Nebenbei hilft sie natürlich auch UTF8 schnell und einfach zu erkennen.
> Aber da die Codes für Ziffern alle 8-bittig sind, kann Dir das egal
> sein.

Nein, BOM ist bei UTF-8 immer EF BB BF. UTF-8 hat kein Problem mit der 
Byteorder (Little- vs. Bigendian) wie UTF-16 oder UTF-32. Mann erkennt 
am BOM nur mit hoher Wahrscheinlichkeit eine UTF-8 codierte Textdatei.

https://de.wikipedia.org/wiki/Byte_Order_Mark

Matthias

von Norbert (Gast)


Lesenswert?

Lothar schrieb:
> Ich habe das Problem jetzt erst mal mit dem Holzhammer gelöst, damit es
> auf Unix und Win 10 läuft. Geht es noch anders?

Sobald etwas anderes als ASCII in deiner (UTF8)Datei steht, wird der 
Holzhammer aber einen dicken, roten Daumen produzieren.
Unter Linux würde ich für so etwas eventuell Funktionen aus der libidn 
benutzen.

›stringprep_utf8_to_locale(…)‹
Convert  string encoded in UTF-8 into the locale's character set by 
using stringprep_convert().

Womöglich gibt es so etwas auch für Windows.

von Noch was (Gast)


Lesenswert?

Benutzt du ein GUI-Framework? Erst mal schauen, ob das Framework ein 
Paket für UTF-8 Ein-Ausgabe mitbringt.

von DPA (Gast)


Lesenswert?

Der UTF-8 BOM ist optional. Ich würde diesen, wenn immer möglich, weg 
lassen.
Auf modernen Linux Systemen verwendet heutzutage eigentlich jeder utf-8. 
GUI Toolkits können in der Regel problemlos damit umgehen, die locale in 
C auch.

Bei meinen C Programmen gehe ich mittlerweile einfach davon aus, das 
utf8 verwendet wird. Wobei das meistens gar keinen unterschied macht.

UTF-8 ist Rückwärtskompatibel zu ASCII. Die Bytes von nicht-ASCII 
Zeichen sind immer grösser als 127/0x1F, also ausserhalb des ASCII 
Bereichs. Nach einem Bytewert für ein ASCII Zeichen zu suchen ist also 
weiterhin sicher. Null Bytes kommen darin auch keine vor. Auch die 
Sortierung nach Bytewerten führt weiterhin nach einer korrekten 
Sortierung nach Codepunkten. Ein Programm, das mit ASCII zurecht kommt, 
kommt also auch mit utf8 zurecht. Nur eben der optionale BOM kann 
manchmal Probleme machen. Dessen Verwendung ist auch nicht empfohlen.

Windows verwendet halt gerne intern aber UTF-16, oder dieses 
fast-UTF-16. Und Windows Programme verwenden gerne Sachen wie z.B. 
wchar_t oder wchar16_t. Deshalb hat man bei Windows Sachen viel eher 
Probleme, als mit Unicode. UTF-8 geht normalerweise einfach, ohne 
weiteres zutuen, da Rückwärtskompatibel usw. Aber mit dem UTF-16 Zeugs 
und den BOMs usw., das fällt einem bei Windows dann halt dann um die 
Ohren.

Μαtthias W. schrieb:
> Nein, BOM ist bei UTF-8 immer EF BB BF. UTF-8 hat kein Problem mit der
> Byteorder (Little- vs. Bigendian) wie UTF-16 oder UTF-32.

Das ist nur halb richtig. Der UTF-8, sowie die beiden UTF-16 BOMs, haben 
einen Unicode Codepoint / Wert, und dieser ist jeweils nicht der selbe! 
Deshalb kann ein UTF-16 BOM als UTF-8 Sequenz kodiert werden. Das ist 
für nichts gut, ausser um Probleme zu verursachen, aber es ist machbar. 
U+FFFE ist EF BF BE. Und U+FEFF ist EF BB BF.

von DerEgon (Gast)


Lesenswert?

DPA schrieb:
> Windows verwendet halt gerne intern aber UTF-16, oder dieses
> fast-UTF-16. Und Windows Programme verwenden gerne Sachen wie z.B.
> wchar_t oder wchar16_t. Deshalb hat man bei Windows Sachen viel eher
> Probleme, als mit Unicode. UTF-8 geht normalerweise einfach, ohne
> weiteres zutuen, da Rückwärtskompatibel usw. Aber mit dem UTF-16 Zeugs
> und den BOMs usw., das fällt einem bei Windows dann halt dann um die
> Ohren.

Das ist historisch begründet; das tut nämlich ernstgemeintes Windows 
schon seit bald 30 Jahren, und da war an UTF-8 oder dergleichen noch 
sehr, sehr lange nicht zu denken. Die damals gewählte Implementierung 
(UCS-2, einfach nur "Unicode" genannt) verwendete konstante 16 Bit pro 
Zeichen, was die Abbildung auf Funktionen wie strcpy/strlen etc. sehr 
einfach machte.

von loeti2 (Gast)


Lesenswert?

DerEgon schrieb:
> Die damals gewählte Implementierung
> (UCS-2, einfach nur "Unicode" genannt) verwendete konstante 16 Bit pro
> Zeichen, was die Abbildung auf Funktionen wie strcpy/strlen etc. sehr
> einfach machte.

Nur der Vollständigkeit halber war es mit Einführung der Surrogate-Bytes 
dann auch mit der Einfachheit vorbei.

Wobei ich trotzdem lieber UTF-16 als UTF-8 verwende.

von DPA (Gast)


Lesenswert?

Ich habe das noch nicht allzu stark getestet, aber hier mal ein Wrapper 
für den TO, der UTF-8 BOMs transparent herausfiltert:
1
#include <stdio.h>
2
#include <stdint.h>
3
4
#define UTF8_BOM "\xEF\xBB\xBF"
5
6
struct utf8_bom_filter {
7
  uint_least8_t match : 2;
8
  uint_least8_t index : 2;
9
  uint_least8_t eof : 1;
10
  uint_least8_t next;
11
};
12
13
int bomless_fgetc(struct utf8_bom_filter*const restrict bfs, FILE*const restrict file){
14
  while(1){
15
    if(bfs->index){
16
      if(bfs->index < bfs->match){
17
        return UTF8_BOM[bfs->index++]&0xFF;
18
      }else if(bfs->index == bfs->match){
19
        bfs->index = 0;
20
        if(bfs->eof || bfs->next != (UTF8_BOM[0]&0xFF)){
21
          bfs->match = 0;
22
          return bfs->eof ? EOF : bfs->next;
23
        }else{
24
          bfs->match = 1;
25
        }
26
      }
27
    }
28
    int ch = fgetc(file);
29
    if((UTF8_BOM[bfs->match]&0xFF) == ch){
30
      if(bfs->match < 2){
31
        bfs->match += 1;
32
      }else{
33
        bfs->match = 0;
34
      }
35
    }else{
36
      if(bfs->match){
37
        bfs->next = ch;
38
        bfs->eof = ch == EOF;
39
        bfs->index = 1;
40
        return UTF8_BOM[0]&0xFF;
41
      }else{
42
        return ch;
43
      }
44
    }
45
  }
46
}
47
48
int main(){
49
  struct utf8_bom_filter bfs;
50
  for(int c; (c=bomless_fgetc(&bfs, stdin)) != EOF;)
51
    putchar(c);
52
}

von Lothar (Gast)


Lesenswert?

loeti2 schrieb:
> Wobei ich trotzdem lieber UTF-16 als UTF-8 verwende

Da habe ich doch gar keinen Einfluss drauf.

Wenn ich unter Win 10 den Notepad aufmache, was schreibe, und speichere, 
wird es UTF-8

von Sebastian (Gast)


Lesenswert?

Lothar schrieb:
> Wenn ich unter Win 10 den Notepad aufmache, was schreibe, und speichere,
> wird es UTF-8

Nimm Notepad++

LG, Sebastian

von c-hater (Gast)


Lesenswert?

Lothar schrieb:

> Da habe ich doch gar keinen Einfluss drauf.

Doch, hast du.

> Wenn ich unter Win 10 den Notepad aufmache, was schreibe, und speichere,
> wird es UTF-8

Das kannst du ändern. Wenn du nicht vollkommen verblödet bist...

Der Notepad von Windows10 ist zwar immer noch ein eher armseliger 
Texteditor, aber Encodings hat er durchaus drauf. Wie das gesamte 
Windows-System...

Gegen völlig inkompetente Benutzer ist er aber, wie jede andere Software 
auch, vollkommen machtlos...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.