rubyforen.de
http://forum.ruby-portal.de/

UTF-8 Erkenner & UTF-8 Zeichenklassenerkenner (Alpha!)
http://forum.ruby-portal.de/viewtopic.php?f=11&t=1479
Seite 1 von 1

Autor:  WoNáDo [ 03 Feb 2006, 22:32 ]
Betreff des Beitrags:  UTF-8 Erkenner & UTF-8 Zeichenklassenerkenner (Alpha!)

Moin, moin!

Da immer wieder Probleme angesprochen wurden die mit dem Unicode-Encoding UTF-8 zusammenhängen, habe ich mal ein kleines Modul dazu geschrieben. Man kann es benutzen um Dateien oder andere Bytefolgen daraufhin zu untersuchen, ob sie korrekte UTF-8-codierte Daten enthalten. Korrekt heisst strukturell korrekt, ein mögliches BOM wird erkannt und Daten enthalten keine Surrogates. Näheres gibts dazu im PDF-Unicode-Buch in Kapitel 2 und 3 unter http://www.unicode.org.

Das ist ein Code-Snippet, welches ich zwar getestet habe, jedoch nicht intensiv mit vielen Testdaten. Sollten Fehler auftreten bitte ich diese hier zu melden. Wer diesen Code kopiert und benutzt tut das auf eigene Verantwortung.

Die Implementierung ist weder sonderlich optimiert noch trickreich - es ist ein Automat, der mittels case-Konstrukten realisiert wurde. Jeder kann sich das Modul seinen Wünschen entsprechend anpassen.

Kern ist die Methode utf8? im Modul UTFtests. Sie wird mit zwei Parametern aufgerufen und kann optional einen Block erhalten.

Eine Kurzbeschreibung steht am Anfang der Methode und die Anwendung sollte durch die beiden Beispiele klar werden.

... bin momentan ziemlich schreibfaul, sofern es nicht Ruby-Code ist :lol:

O.K. - Der Inhalt der in den Beispielen als UTFtests.rb zitierten Datei.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
module UTFtests

def utf8?(reader, lvl)
# reader - muss 'each_with_address'-Methode haben, die zwei Werte an den Block
# uebergibt. Einen Adresswert und ein Byte als Zahlenwert.
# Level gibt den Report-Level an. Falls ein Block vorhanden ist wird er mit zwei Parametern
# versorgt. Den aktuellen Level und ein dazu passendes Array. Der Block wird nur aufgerufen, wenn
# 'lvl' kleiner oder gleich dem yield-level ist.
# level 0 - [addr, byte] - fuer jedes verarbeitete Byte
# level 1 - [Codepoint] - fuer jeden korrekten Unicode-Codepoint
# level 2 - [n_of_bytes, n_of_chars] - Anzahl verarbeiteter Bytes und Anzahl korrekter Unicode-Codepoints
# level 3 - [text] Textmeldung: BOM gefunden, Fehler sind aufgetreten, ...
# level 4 - [addr, byte] - Byte 'byte' an falscher Position bei Adresse 'addr'
first = true
errors = false
bytes = Array.new(3)
charerror = false
foundBOM = false
n_of_chars = 0
n_of_bytes = 0
block = block_given??true:false
state = 'Start'

reader.each_with_address do |addr, byte|

n_of_bytes += 1
yield 0, [addr, byte] if block && lvl <= 0

# Suche Wiederaufsatzpunkt nach fehlerhaftem Byte
if charerror && (byte <= 0x7F || byte >= 0xC2) && byte <= 0xF4
charerror = false
errors = true
state = 'Start'
elsif charerror
errors = true
next
end

# Der Automat als Case-Construct
case state

# Initialer State und neues Zeichen erwartet
when 'Start'
bytes[0] = byte
case byte
when 0xEF
if first
state = 'BOMmiddle'
else
state = 'ThreeByteMiddle2'
end
when 0x00..0x7F
n_of_chars += 1
yield 1, [bytes[0]] if block && lvl <= 1
when 0xC2..0xDF
state = 'TwoByteLast'
when 0xE0
state = 'ThreeByteMiddle1'
when 0xE1..0xEF
state = 'ThreeByteMiddle2'
when 0xF0
state = 'FourByteSecond1'
when 0xF1..0xF4
state = 'FourByteSecond2'
else
charerror = true
end # case byte

# Eventuell kommt mittleres BOM-Byte
when 'BOMmiddle'
bytes[1] = byte
case byte
when 0xBB
state = 'BOMlast'
when 0x80..0xBF
state = 'ThreeByteLast'
else
charerror = true
end # case byte

# Eventuell BOM gefunden
when 'BOMlast'
case byte
when 0xBF
state = 'Start'
foundBOM = true
yield 3, ["Found a UTF-8 Byte Order Mark (BOM)"] if block && lvl <= 3
when 0x80..0xBF
state = 'Start'
n_of_chars += 1
yield 1, [((bytes[0] & 0x0F) << 12) + ((bytes[1] & 0x3F) << 6) +
(byte & 0x3F)] if block && lvl <= 1
else
charerror = true
end # case byte

# Zweites und letztes Byte erwartet
when 'TwoByteLast'
case byte
when 0x80..0xBF
state = 'Start'
n_of_chars += 1
yield 1, [((bytes[0] & 0x1F) << 6) + (byte & 0x3F)] if block && lvl <= 1
else
charerror = true
end # case byte

# Mittleres Byte erwartet
when 'ThreeByteMiddle1'
bytes[1] = byte
case byte
when 0xA0..0xBF
state = 'ThreeByteLast'
else
charerror = true
end # case byte

# Mittleres Byte erwartet
when 'ThreeByteMiddle2'
bytes[1] = byte
case byte
when 0x80..0xBF
state = 'ThreeByteLast'
else
charerror = true
end # case byte

# Drittes und letztes Byte erwartet
when 'ThreeByteLast'
case byte
when 0x80..0xBF
state = 'Start'
n_of_chars += 1
yield 1, [((bytes[0] & 0x0F) << 12) +((bytes[1] & 0x3F) << 6) +
(byte & 0x3F)] if block && lvl <= 1
else
charerror = true
end # case byte

# Zweites von vier Bytes erwartet
when 'FourByteSecond1'
bytes[1] = byte
case byte
when 0x90..0xBF
state = 'FourByteThird'
else
charerror = true
end # case byte

# Zweites von vier Bytes erwartet
when 'FourByteSecond2'
bytes[1] = byte
case byte
when 0x80..0xBF
state = 'FourByteThird'
else
charerror = true
end # case byte

# Drittes von vier Bytes erwartet
when 'FourByteThird'
bytes[2] = byte
case byte
when 0x80..0xBF
state = 'FourByteLast'
else
charerror = true
end # case byte

# Viertes und letztes von vier Bytes erwartet
when 'FourByteLast'
case byte
when 0x80..0xBF
state = 'Start'
n_of_chars += 1
yield 1, [((bytes[0] & 0x07) << 18) + ((bytes[1] & 0x3F) << 12) +
((bytes[2] & 0x3F) << 6) + (byte & 0x3F)] if block && lvl <= 1
else
charerror = true
end # case byte

# Unm��glicher Fehler ;-))
else
raise "+++ utf8? - internal error: unknown state"
return false

end # case state

# BOM wird nur am Anfang als solches erkannt
first = false
if charerror
yield 4, [addr, byte] if block && lvl <= 4
end # if

end # each_with_address

# Start-State ist auch einzig korrekter End-State
yield 2, [n_of_bytes, n_of_chars] if block && lvl <= 2
if state == 'Start' && !charerror && !errors
true
else
yield 3, ["Incorrect UTF-8 encoded data"] if block && lvl <= 3
false
end # if

end # utf8?

end # UTFtests

Ein erster Test benutzt einen im Programm angegebenen String.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
require "UTFtests.rb"
include UTFtests

class Ostr
def initialize(str)
@buff = str
end
def each_with_address
position = -1
@buff.each_byte{|byte|yield position+=1, byte}
end
end

erg = utf8?(Ostr.new("\xEF\xBB\xBFblabla\xE0\x9F\x80blabla\xEF\xBB\xBF"), 0) do |lvl, inf|
case lvl
when 0
puts "Adress: #{"%08X" % inf[0]}, Byte: #{"%02X" % inf[1]}"
when 1
puts "Codepoint #{"%08X" % inf[0]}"
when 2
puts "#{inf[0]} Bytes processed, #{inf[1]} correct Codepoints (without a possible BOM)"
when 3
puts "#{inf[0]}"
when 4
puts "+++ Illegal byte, address: #{"%08X" % inf[0]}, content: #{"%02X" % inf[1]}"
end
end

puts "utf8?-Ergebnis: #{erg}"
ergibt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Adress: 00000000, Byte: EF
Adress: 00000001, Byte: BB
Adress: 00000002, Byte: BF
Found a UTF-8 Byte Order Mark (BOM)
Adress: 00000003, Byte: 62
Codepoint 00000062
Adress: 00000004, Byte: 6C
Codepoint 0000006C
Adress: 00000005, Byte: 61
Codepoint 00000061
Adress: 00000006, Byte: 62
Codepoint 00000062
Adress: 00000007, Byte: 6C
Codepoint 0000006C
Adress: 00000008, Byte: 61
Codepoint 00000061
Adress: 00000009, Byte: E0
Adress: 0000000A, Byte: 9F
+++ Illegal byte, address: 0000000A, content: 9F
Adress: 0000000B, Byte: 80
Adress: 0000000C, Byte: 62
Codepoint 00000062
Adress: 0000000D, Byte: 6C
Codepoint 0000006C
Adress: 0000000E, Byte: 61
Codepoint 00000061
Adress: 0000000F, Byte: 62
Codepoint 00000062
Adress: 00000010, Byte: 6C
Codepoint 0000006C
Adress: 00000011, Byte: 61
Codepoint 00000061
Adress: 00000012, Byte: EF
Adress: 00000013, Byte: BB
Adress: 00000014, Byte: BF
Codepoint 0000FEFF
21 Bytes processed, 13 correct Codepoints (without a possible BOM)
Incorrect UTF-8 encoded data
utf8?-Ergebnis: false

Der zweite Test liest aus einer Datei. Bitte beachten, dass für korrekte Ergebnisse unter Windows die Datei im Binary Mode eröffnet werden muss - also "rb".


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
require "UTFtests.rb"
include UTFtests

class Fstr
def initialize(name)
@buff = File.new(name, "rb").read
end
def each_with_address
position = -1
@buff.each_byte{|byte|yield position+=1, byte}
end
end

erg = utf8?(Fstr.new("utf8datei.txt"), 2) do |lvl, inf|
case lvl
when 0
puts "Adress: #{"%08X" % inf[0]}, Byte: #{"%02X" % inf[1]}"
when 1
puts "Codepoint #{"%08X" % inf[0]}"
when 2
puts "#{inf[0]} Bytes processed, #{inf[1]} correct Codepoints (without a possible BOM)"
when 3
puts "#{inf[0]}"
when 4
puts "+++ Illegal byte, address: #{"%08X" % inf[0]}, content: #{"%02X" % inf[1]}"
end
end

puts "utf8?-Ergebnis: #{erg}"
ergibt

1
2
3
Found a UTF-8 Byte Order Mark (BOM)
51 Bytes processed, 47 correct Codepoints (without a possible BOM)
utf8?-Ergebnis: true
bei folgendem Dateiinhalt

1
2
Hall��chen,
dies ist eine UTF-8-codierte Datei
.
Alles weitere auf Anfrage hier im Forum in der Mitte des Universums

Autor:  murphy [ 04 Feb 2006, 00:07 ]
Betreff des Beitrags: 

hast du vor, das mal auf rubyforge zu veröffentlichen?

Autor:  WoNáDo [ 04 Feb 2006, 00:18 ]
Betreff des Beitrags: 

murphy hat geschrieben:
hast du vor, das mal auf rubyforge zu veröffentlichen?

Macht das denn Sinn? - Da müsste noch ein bisschen mehr rein: Check und Analyse für alle Formate, Konvertierungen, usw.

Momentan weiss ich nicht wann ich dazu komme. Wir ziehen gerade sehr kurzfristig um - da ist Zeit nicht einplanbar. Im März wird es dann eher gehen.

Allerdings freue ich mich über Wünsche (=realisierbare Wünsche, keine Handbücher :wink: ) bezüglich Bedarf.

>>>>> EDIT - 04.02.2006 10:13 >>>>>

Ich werde heute nachmittag ein Projekt "UTF-test-convert" (o.ä. Namen) bei RubyForge einrichten und stückweise Code eintragen. Ich sehe ein, dass das Sinn macht.

Allerdings muss ich den Code noch etwas verbessern (z.B. als States keine Strings sondern Symbole) und das Design auch noch.

btw...

Ich habe da noch ein Problem. Das soll ja OO werden und ich grüble nocjh an einem sinnvollen Klassenmodell. Eigentlich wäre das von utf8? zu bearbeitende Objekt so etwas wie Bytesequenz, da es erst nach einer Prüfung zu einem UTF-8-Objekt werden kann (ähnliches für die anderen Encodings). Bei dem reinen Check ist das zwar noch nicht so wesentlich, beim Konvertieren aber wohl.

Ausserdem wäre eine logisch sinnvolle Strukturierung noch so, dass UnicodeText ein Container von Codepoints wäre, die wiederum auf deutlich unterschiedliche Arten repräsentiert werden (/UTF-[8|16|32][BE|LE|)/). Eine virtuelle Hierarchie wegen der Zeichenkombinationen ist für manche Tests auch noch sinnvoll.

Hat irgend jemand von Euch Erfahrungen mit derartigen OO-Designvorgaben und Umsetzungen - und - evtl. ein paar Ideen dazu?

Autor:  WoNáDo [ 06 Feb 2006, 19:18 ]
Betreff des Beitrags: 

Ich hab das jetzt als Projekt in RubyForge angemeldet. Es steht dann demnächst unter http://rubyforge.org/projects/utftest/ - ich schätze, dass ich zum Wochenende hin dazu komme.

Autor:  azuby [ 27 Nov 2007, 09:56 ]
Betreff des Beitrags: 

Der Rubyforge-Link lautet: http://rubyforge.org/projects/utf-test/ also noch mit einem "-" dazwischen.

azuby

Autor:  WoNáDo [ 27 Nov 2007, 12:19 ]
Betreff des Beitrags: 

azuby hat geschrieben:
Der Rubyforge-Link lautet: http://rubyforge.org/projects/utf-test/ also noch mit einem "-" dazwischen.

Ja und Nein :wink:
Das alte Projekt hatte ich gelöscht, weil absolut nicht klar war, was da im Detail geschehen sollte. Das hier ist ein jetzt neu angemeldetes, welches erst Zusammenhang mit Ruby 1.9.1 laufen wird, aber nicht mehr unter Ruby 1.8. Ob es irgendwann noch eine Variante für Ruby 1.8 geben wird, weiss ich nicht.

Autor:  WoNáDo [ 27 Nov 2007, 18:23 ]
Betreff des Beitrags: 

So...

azuby hat mich etwas inspiriert eine erste Test-Erweiterung in Vorschau auf das, was da noch für Ruby 1.9 auf RubyForge kommen soll, zu erstellen - hier ist das erste Ergebnis. Sollte jemand auf Fehler stossen, bitte ich um Meldung in diesem Thread. Hier die Ausgabe einer Demoanwendung...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Wolfgang>utf8_blocks
Correct usage is 'utf8_blocks <filename>'
or 'ruby utf8_blocks.rb <filename>'

Wolfgang>utf8_blocks otto
+++++ File 'otto' does not exist

Wolfgang>utf8_blocks utf8laenger.txt
----------------------------------------------------------------
Start der Analyse der utf-8-codierten Datei 'utf8laenger.txt'
----------------------------------------------------------------
Found a UTF-8 Byte Order Mark (BOM)
----------------------------------------------------------------
Ergebnis der Analyse der utf-8-codierten Datei 'utf8laenger.txt'
----------------------------------------------------------------
BOM or ZWNBSP..................................... 1
Basic Latin....................................... 10807
----------------------------------------------------------------

Wolfgang>utf8_blocks keinutf8.txt
-------------------------------------------------------------
Start der Analyse der utf-8-codierten Datei 'keinutf8.txt'
-------------------------------------------------------------
+++ Illegal byte, address: 00000001, content: F6
-------------------------------------------------------------

Wolfgang>to_unix utf8_blocks.rb

Wolfgang>utf8_blocks-unix keinutf8.txt
-------------------------------------------------------------
Start der Analyse der utf-8-codierten Datei 'keinutf8.txt'
-------------------------------------------------------------
+++ Illegal byte, address: 00000001, content: F6
-------------------------------------------------------------

Wolfgang>utf8_blocks-unix utf8laenger.txt
----------------------------------------------------------------
Start der Analyse der utf-8-codierten Datei 'utf8laenger.txt'
----------------------------------------------------------------
Found a UTF-8 Byte Order Mark (BOM)
----------------------------------------------------------------
Ergebnis der Analyse der utf-8-codierten Datei 'utf8laenger.txt'
----------------------------------------------------------------
BOM or ZWNBSP..................................... 1
Basic Latin....................................... 10807
----------------------------------------------------------------

Wolfgang>


DIESES PROGRAMM BEFINDET SICH IN DER ENTWICKLUNG, ALSO IM ALPHA-STADIUM!

ICH ÜBERNEHME KEINERLEI GEWÄHRLEISTUNG FÜR EINE KORREKTE FUNKTION UND AUCH KEINERLEI HAFTUNG FÜR FOLGESCHÄDEN DIE SICH DURCH EINE ANWENDUNG EVENTUELL ERGEBEN.

DIE SOFTWARE STEHT IN DIESER ENTWICKLUNGSVERSION UNTER LGPL-LIZENZ.

BEI DER BENUTZUNG MÜSSEN AUCH DIE BENUTZUNGSHINWEISE DES UNICODE-KONSORTIUMS UNTER http://www.unicode.org/terms_of_use.html BEACHTET WERDEN.


Dateianhänge:
Dateikommentar: Programm im Apha-Stadium mit *nix-Zeilenenden.
utf8_blocks-unix.rb [18.72 KiB]
246-mal heruntergeladen
Dateikommentar: Programm im Apha-Stadium mit Windows-Zeilenenden.
utf8_blocks.rb [19.16 KiB]
240-mal heruntergeladen

Autor:  shevegen [ 04 Sep 2008, 02:05 ]
Betreff des Beitrags: 

Ist schon etwas alt hier ;) aber da ich mich erinnere das Woonado immer mal Probleme mit UTF hat bzw die Komplexität davon kritisiert, hier mal von einem Perl Entwickler seine Problemchen (aber halt mit perl... is halt der alte Bruder von Ruby von dem man lernen kann)


http://jeremy.zawodny.com/blog/archives/010546.html

Autor:  WoNáDo [ 04 Sep 2008, 10:43 ]
Betreff des Beitrags: 

shevegen hat geschrieben:
...Probleme mit UTF hat bzw die Komplexität davon kritisiert...

Das ist ein Missverständnis. Ich finde die Klassifizierungs- und Testmöglichkeiten bei Unicode ausgesprochen interessant. Die bis heute (Ruby 1.9 ist noch nicht stabil vorhanden) mangelhafte Ruby-Unterstützung stört mich.

Autor:  WoNáDo [ 07 Nov 2016, 01:07 ]
Betreff des Beitrags:  Re: UTF-8 Erkenner & UTF-8 Zeichenklassenerkenner (Alpha!)

Habe vorhin erstaunt feststellen können, dass der Zeichenklassenerkenner zumindest in der *nix-Version immer noch läuft, und zwar bei mir unter ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux].

ich brauchte dringend eine derartige Funktionalität un da fiel mir ein, dass ich vor Ewigkeiten so etwas mal gebaut und hier veröffentlicht habe. Nach ein wenig Sucherei wurde ich fündig und habe es dann auf die entpackte Version von http://downloads.dbpedia.org/2015-10/core-i18n/en/mappingbased_objects_en.ttl.bz2 angewandt. Nach 55 Minuten Laufzeit für die 2,5GB in utf-8 codierte Textdatei erhielt ich das Ergebnis. Da das Programm nie für derartige Dateigrössen optimiert war (die gab es vor 9 Jahren ja auch eher weniger), empfand ich das nicht einmal als langsam.

Erstaunlich - nach 9 Jahren und x Ruby-Versionen geht es immer noch :-)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
>>> #
>>> # Find used code blocks in input data
>>> #
>>> ./utf8_blocks-unix.rb mappingbased_objects_en.ttl
----------------------------------------------------------------------------
Start der Analyse der utf-8-codierten Datei 'mappingbased_objects_en.ttl'
----------------------------------------------------------------------------
----------------------------------------------------------------------------
Ergebnis der Analyse der utf-8-codierten Datei 'mappingbased_objects_en.ttl'
----------------------------------------------------------------------------
Arabic............................................ 831
Arabic Presentation Forms-B....................... 2
Arrows............................................ 78
Basic Latin.......................................2449631147
Bengali........................................... 38
CJK Symbols and Punctuation....................... 14
CJK Unified Ideographs............................ 2339
Combining Diacritical Marks....................... 48
Currency Symbols.................................. 19
Cyrillic.......................................... 3259
Devanagari........................................ 129
General Punctuation............................... 358693
Greek Extended.................................... 3
Greek and Coptic.................................. 839
Gujarati.......................................... 76
Halfwidth and Fullwidth Forms..................... 85
Hangul Syllables.................................. 203
Hebrew............................................ 48
Hiragana.......................................... 124
IPA Extensions.................................... 8773
Katakana.......................................... 383
Latin Extended Additional......................... 20903
Latin Extended-A.................................. 1224890
Latin Extended-B.................................. 97531
Latin-1 Supplement................................ 2317318
Letterlike Symbols................................ 63
Malayalam......................................... 119
Mathematical Operators............................ 1259
Miscellaneous Mathematical Symbols-A.............. 42
Miscellaneous Symbols............................. 171
Myanmar........................................... 9
Number Forms...................................... 67
Sinhala........................................... 39
Small Form Variants............................... 2
Spacing Modifier Letters.......................... 1909
Supplemental Mathematical Operators............... 1
Tamil............................................. 48
Telugu............................................ 22
Thai.............................................. 64
----------------------------------------------------------------------------
>>>

Seite 1 von 1 Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]
Powered by phpBB® Forum Software © phpBB Group
http://www.phpbb.com/