############################################################################# ## #W UnicodeTools.gi GAPDoc Frank Lübeck ## ## #Y Copyright (C) 2007, Frank Lübeck, Lehrstuhl D für Mathematik, #Y RWTH Aachen ## ## The files UnicodeTools.g{d,i} contain utilities for converting text ## between different encodings. They introduce unicode strings and ## characters as GAP objects. ##
# reading some extendible tables of translations of unicode characters
ReadPackage("GAPDoc", "lib/UnicodeTabs.g");
# UNICODE_RECODE is a is a record. For some string enc, describing a # character encoding, UNICODE_RECODE.(enc) is a function which translates # a GAP string in encoding enc to a list of integers describing the unicode # codepoints of the characters in the string. # # UNICODE_RECODE.TABLES contains some embeddings of 8 bit code pages into # unicode as lists of length 256, codepoint of character i in [0..255] in # position i+1.
UNICODE_RECODE.NormalizedEncodings.("UTF-8") := "UTF-8";
UNICODE_RECODE.NormalizedEncodings.("XML") := "XML"; for nam in RecNames(UNICODE_RECODE.NormalizedEncodings) do
UNICODE_RECODE.NormalizedEncodings.(LowercaseString(nam)) :=
UNICODE_RECODE.NormalizedEncodings.(nam);
od; end; # can be used for case insensitive normalization
UNICODE_RECODE.NormalizedEncoding := function(str)
local lstr;
lstr := LowercaseString(str); if not IsBound(UNICODE_RECODE.NormalizedEncodings.(lstr)) then
return fail; else
return UNICODE_RECODE.NormalizedEncodings.(lstr); fi; end;
UNICODE_RECODE.f();
Unbind(UNICODE_RECODE.f); # slightly more efficient for latin1:
UNICODE_RECODE.Decoder.("ISO-8859-1") := function(str)
return List(str, INT_CHAR); end; # helper function; arg: str[, start], translate single UTF-8 character # to its unicode number
UNICODE_RECODE.UnicodeUTF8Char := function(arg)
local str, i, a, i1, i2, i3;
str := arg[1]; if Length(arg)>1then
i := arg[2]; else
i := 1; fi;
a := INT_CHAR(str[i]); if a < 128then
return a; elif a < 224then if i = Length(str) then return fail; fi;
i1 := INT_CHAR(str[i+1]); if i1 < 128 or i1 > 191then
return fail; fi;
return (a mod 192)*64 + (i1 mod 64); elif a < 240then if Length(str) < i+2then return fail; fi;
i1 := INT_CHAR(str[i+1]);
i2 := INT_CHAR(str[i+2]); if i1 < 128 or i2 < 128 or i1 > 191 or i2 > 191then
return fail; fi;
return (a mod 224)*4096 + (i1 mod 64)*64
+ (i2 mod 64); else if Length(str) < i+3then return fail; fi;
i1 := INT_CHAR(str[i+1]);
i2 := INT_CHAR(str[i+2]);
i3 := INT_CHAR(str[i+3]); if i1<128 or i2<128 or i3<128 or i1>191 or i2>191 or i3>191then
return fail; fi;
return (a mod 240)*262144 + (i1 mod 64)*4096
+ (i2 mod 64)*64
+ (i3 mod 64); fi; end; ## UNICODE_RECODE.Decoder.("UTF-8") := function(str) ## local res, c, i; ## res := []; ## for i in [1..Length(str)] do ## c := INT_CHAR(str[i]); ## if c < 128 or c > 191 then ## Add(res, UNICODE_RECODE.UnicodeUTF8Char(str, i)); ## fi; ## od; ## if fail in res then return fail; fi; ## return res; ## end;
UNICODE_RECODE.Decoder.("UTF-8") := function(str)
local res, i, n;
res := [];
i := 1; while i <= Length(str) do
n := UNICODE_RECODE.UnicodeUTF8Char(str, i); if n = fail then
return fail; elif n < 128then
i := i+1; elif n < 2048then
i := i+2; elif n < 65536then
i := i+3; else
i := i+4; fi;
Add(res, n);
od; if fail in res then return fail; fi;
return res; end;
UNICODE_RECODE.Decoder.("XML") := function(str)
local res, i, j, n;
res := [];
i := 1; while i <= Length(str) do if str[i] = '&' and i < Length(str) and str[i+1] = '#' then
j := Position(str, ';', i);
n := str{[i+2..j-1]}; if n[1] = 'x'then
n := IntHexString(n{[2..Length(n)]}); else
n := Int(n); fi;
Add(res, n);
i := j+1; else
Add(res, INT_CHAR(str[i]));
i := i+1; fi;
od;
return res; end;
UNICODE_RECODE.Decoder.("URL") := function(str)
local res, i;
res := "";
i := 1; while i <= Length(str) do if str[i] = '%'then
Add(res, CHAR_INT(IntHexString(str{[i+1,i+2]})));
i := i+3; else
Add(res, str[i]);
i := i+1; fi;
od;
return IntListUnicodeString(Unicode(res, "UTF-8")); end;
# wrap and cache integers as unicode characters
InstallMethod(UChar, [IsInt], function(n)
local res; if not IsInt(n) or n < 0 or n > 2097151then
return fail; fi; if IsBound(UNICODECHARCACHE[n]) then
return UNICODECHARCACHE[n]; fi;
res := rec(codepoint := n);
Objectify(UnicodeCharacterType, res);
UNICODECHARCACHE[n] := res;
return res; end); # interpret GAP characters as latin 1 encoded
InstallMethod(UChar, [IsChar], function(c)
return UChar(INT_CHAR(c)); end);
## <#GAPDoc Label="Unicode"> ## <ManSection> ## <Heading>Unicode Strings and Characters</Heading> ## <Oper Name="Unicode" Arg="list[, encoding]"/> ## <Oper Name="UChar" Arg="num"/> ## <Filt Name="IsUnicodeString" /> ## <Filt Name="IsUnicodeCharacter" /> ## <Func Name="IntListUnicodeString" Arg="ustr" /> ## ## <Description> ## Unicode characters are described by their <Emph>codepoint</Emph>, an ## integer in the range from <M>0</M> to <M>2^{21}-1</M>. ## For details about unicode, see <URL>https://www.unicode.org</URL>.<P/> ## ## The function <Ref Oper="UChar"/> wraps an integer <A>num</A> into ## a &GAP; object lying in the filter <Ref Filt="IsUnicodeCharacter"/>. ## Use <C>Int</C> to get the codepoint back. The argument <A>num</A> can ## also be a &GAP; character which is then translated to an integer via ## <Ref BookName="Reference" Func="IntChar"/>. <P/> ## ## <Ref Oper="Unicode" /> produces a &GAP; object in the filter ## <Ref Filt="IsUnicodeString"/>. This is a wrapped list of integers ## for the unicode characters in the string. The function <Ref ## Func="IntListUnicodeString"/> gives access to this list of integers. ## Basic list functionality is available for <Ref Filt="IsUnicodeString"/> ## elements. The entries are in <Ref Filt="IsUnicodeCharacter"/>. ## The argument <A>list</A> for <Ref Oper="Unicode"/> is either a list of ## integers or a &GAP; string. In the latter case an <A>encoding</A> can be ## specified as string, its default is <C>"UTF-8"</C>. <P/> ## ## <Index>URL encoding</Index><Index>RFC 3986</Index> ## Currently supported encodings can be found in ## <C>UNICODE_RECODE.NormalizedEncodings</C> (ASCII, ## ISO-8859-X, UTF-8 and aliases). The encoding <C>"XML"</C> means an ASCII ## encoding in which non-ASCII characters are specified by XML character ## entities. The encoding <C>"URL"</C> is for URL-encoded (also called ## percent-encoded strings, as specified in RFC 3986 ## (<URL Text="see here">https://www.ietf.org/rfc/rfc3986.txt</URL>). ## The listed encodings <C>"LaTeX"</C> and aliases ## cannot be used with <Ref Oper="Unicode" />. ## See the operation <Ref Oper="Encode"/> for mapping a unicode string ## to a &GAP; string.<P/> ## <Example> ## gap> ustr := Unicode("a and \366", "latin1"); ## Unicode("a and ö") ## gap> ustr = Unicode("a and ö", "XML"); ## true ## gap> IntListUnicodeString(ustr); ## [ 97, 32, 97, 110, 100, 32, 246 ] ## gap> ustr[7]; ## 'ö' ## </Example> ## </Description> ## </ManSection> ## ## <#/GAPDoc>
# NC method, assume that l is (plain?) list of integers in correct range
InstallMethod(Unicode, [IsList], function(l)
local res;
res := [l];
Objectify(UnicodeStringType, res);
return res; end); # extract the list of integers
InstallGlobalFunction("IntListUnicodeString", function(ustr)
return ustr![1]; end);
InstallMethod(Unicode, [IsString, IsString], function(str, enc)
local res; if Length(str) > 0 and not IsStringRep(str) then ## Info(InfoWarning, 1, "#W Changing argument to IsStringRep"); ## Info(InfoWarning, 2, ":\n ", str); ## Info(InfoWarning, 1, "\n"); ## ConvertToStringRep(str);
str := ShallowCopy(str);
ConvertToStringRep(str); fi; if not IsBound(UNICODE_RECODE.NormalizedEncodings.(enc)) then
Error("Sorry, only the following encodings are supported for 'Unicode':\n",
RecNames(UNICODE_RECODE.Decoder), "\n"); fi;
enc := UNICODE_RECODE.NormalizedEncodings.(enc);
res := UNICODE_RECODE.Decoder.(enc)(str); if res = fail then
return fail; fi;
return Unicode(res); end); # just a string as argument is assumed to be in UTF-8 encoding
InstallMethod(Unicode, [IsStringRep], function(str)
return Unicode(str, "UTF-8"); end);
# view and print
InstallMethod(ViewObj, [IsUnicodeString], function(ustr)
local l;
l := IntListUnicodeString(ustr); if Length(l) > 40then
l := l{[1..37]};
Append(l, [46,46,46]); fi;
Print("Unicode(");
ViewObj(Concatenation(List(l, UNICODE_RECODE.UTF8UnicodeChar)));
Print(")"); end);
InstallMethod(PrintObj, [IsUnicodeString], function(ustr)
Print("Unicode(");
PrintObj(Concatenation(List(IntListUnicodeString(ustr),
UNICODE_RECODE.UTF8UnicodeChar)));
Print(")"); end);
# the *basic* list operations
InstallMethod(Length, [IsUnicodeString], function(ustr)
return Length(IntListUnicodeString(ustr)); end);
InstallOtherMethod(\[\]\:\=, [IsUnicodeString and IsMutable,
IsPosInt, IsUnicodeCharacter], function(ustr, i, x)
local l; if i > Length(ustr)+1then
Error("no unicode string assignment at position ",i,"\n"); fi;
l := IntListUnicodeString(ustr);
l[i] := x!.codepoint; end);
InstallMethod(Unbind\[\], [IsUnicodeString and IsMutable, IsPosInt], function(ustr, i)
local l; if i < Length(ustr) then
Error("can only unbind last character in unicode string\n"); fi; if i = Length(ustr) then
l := IntListUnicodeString(ustr);
Unbind(l[Length(l)]); fi; end);
# let ShallowCopy produce a unicode string
InstallMethod(ShallowCopy, [IsUnicodeString], function(ustr)
return Unicode(ShallowCopy(IntListUnicodeString(ustr))); end); # let sublists be unicode strings
InstallMethod(\{\}, [IsUnicodeString, IsList], function(ustr, poss)
return Unicode(IntListUnicodeString(ustr){poss}); end);
# a better Append for efficiency
InstallMethod(Append, [IsUnicodeString and IsMutable, IsUnicodeString], function(ustr, ustr2)
Append(IntListUnicodeString(ustr), IntListUnicodeString(ustr2)); end);
## <#GAPDoc Label="Encode"> ## <ManSection> ## <Oper Name="Encode" Arg="ustr[, encoding]" /> ## <Returns>a &GAP; string</Returns> ## <Func Name="SimplifiedUnicodeString" Arg='ustr[, encoding][, "single"]' /> ## <Returns>a unicode string</Returns> ## <Func Name="LowercaseUnicodeString" Arg="ustr" /> ## <Returns>a unicode string</Returns> ## <Func Name="UppercaseUnicodeString" Arg="ustr" /> ## <Returns>a unicode string</Returns> ## <Var Name="LaTeXUnicodeTable" /> ## <Var Name="SimplifiedUnicodeTable" /> ## <Var Name="LowercaseUnicodeTable" /> ## ## <Description> ## The operation <Ref Oper="Encode"/> translates a unicode string <A>ustr</A> ## into a &GAP; string in some specified <A>encoding</A>. The default ## encoding is <C>"UTF-8"</C>. <P/> ## ## Supported encodings can be found in ## <C>UNICODE_RECODE.NormalizedEncodings</C>. Except for some cases ## mentioned below characters which are not available in the target ## encoding are substituted by '?' characters.<P/> ## ## If the <A>encoding</A> is <C>"URL"</C> (see <Ref Oper="Unicode"/>) then ## an optional argument <A>encreserved</A> can be given, it must be a list ## of reserved characters which should be percent encoded; the default is ## to encode only the <C>%</C> character.<P/> ## ## The encoding <C>"LaTeX"</C> substitutes ## non-ASCII characters and &LaTeX; special characters by &LaTeX; code ## as given in an ordered list ## <C>LaTeXUnicodeTable</C> of pairs [codepoint, string]. If you have a ## unicode character for which no substitution is contained in that list, ## you will get a warning and the translation is <C>Unicode(nr)</C>. ## In this case find a substitution and add a ## corresponding [codepoint, string] ## pair to <C>LaTeXUnicodeTable</C> using <Ref BookName="reference" ## Oper="AddSet"/>. Also, please, tell the &GAPDoc; authors about your ## addition, such that we can extend the list <C>LaTeXUnicodeTable</C>. ## (Most of the initial entries were generated from lists in the ## &TeX; projects enc&TeX; and <C>ucs</C>.) ## There are some variants of this encoding:<P/> ## <C>"LaTeXleavemarkup"</C> does ## the same translations for non-ASCII characters but leaves the &LaTeX; ## special characters (e.g., any &LaTeX; commands) as they are.<P/> ## <C>"LaTeXUTF8"</C> does not give a warning about unicode characters ## without explicit translation, instead it translates the character ## to its <C>UTF-8</C> encoding. Make sure to setup your &LaTeX; document such ## that all these characters are understood.<P/> ## <C>"LaTeXUTF8leavemarkup"</C> is a combination of the last two variants.<P/> ## ## Note that the <C>"LaTeX"</C> encoding can only be used with <Ref ## Oper="Encode"/> but not for the opposite translation with <Ref ## Oper="Unicode" /> (which would need far too complicated heuristics).<P/> ## ## The function <Ref Func="SimplifiedUnicodeString"/> can be used to ## substitute many non-ASCII characters by related ASCII characters ## or strings (e.g., by a corresponding character without accents). ## The argument <A>ustr</A> and the result are unicode strings, if ## <A>encoding</A> is <C>"ASCII"</C> then all non-ASCII characters are ## translated, otherwise only the non-latin1 characters. If the string ## <C>"single"</C> in an argument then only substitutions are considered ## which don't make the result string longer. The translations are stored ## in a sorted list <C>SimplifiedUnicodeTable</C>. Its entries are of the ## form <C>[codepoint, trans1, trans2, ...]</C>. Here <C>trans1</C> and so ## on is either an integer for the codepoint of a substitution character or ## it is a list of codepoint integers. If you are missing characters in ## this list and know a sensible ASCII approximation, then add an entry ## (with <Ref BookName="reference" Oper="AddSet"/>) and tell the &GAPDoc; ## authors about it. (The initial content of <C>SimplifiedUnicodeTable</C> ## was mainly generated from the <Q><C>transtab</C></Q> tables by Markus ## Kuhn.)<P/> ## ## The function <Ref Func="LowercaseUnicodeString"/> gets and returns a ## unicode string and translates each uppercase character to its ## corresponding lowercase version. This function uses a list ## <C>LowercaseUnicodeTable</C> of pairs of codepoint integers. ## This list was generated using the file <F>UnicodeData.txt</F> from the ## unicode definition (field 14 in each row).<P/> ## ## The function <Ref Func="UppercaseUnicodeString"/> does the similar ## translation to uppercase characters. ## ## <Example> ## gap> ustr := Unicode("a and ö", "XML"); ## Unicode("a and ö") ## gap> SimplifiedUnicodeString(ustr, "ASCII"); ## Unicode("a and oe") ## gap> SimplifiedUnicodeString(ustr, "ASCII", "single"); ## Unicode("a and o") ## gap> ustr2 := UppercaseUnicodeString(ustr);; ## gap> Print(Encode(ustr2, GAPInfo.TermEncoding), "\n"); ## A AND Ö ## </Example> ## </Description> ## </ManSection> ## ## <#/GAPDoc> ##
# helper function for encoding a unicode character to UTF-8
UNICODE_RECODE.UTF8UnicodeChar := function(n)
local res, a, b, c, d;
res := ""; if n < 0then
return fail; elif n < 128then
Add(res, CHAR_INT(n)); elif n < 2048then
a := n mod 64;
b := (n - a) / 64;
Add(res, CHAR_INT(b + 192));
Add(res, CHAR_INT(a + 128)); elif n < 65536then
a := n mod 64;
n := (n - a)/64;
b := n mod 64;
c := (n - b)/64;
Add(res, CHAR_INT(c + 224));
Add(res, CHAR_INT(b + 128));
Add(res, CHAR_INT(a + 128)); elif n < 2097152then
a := n mod 64;
n := (n - a)/64;
b := n mod 64;
n := (n - b)/64;
c := n mod 64;
d := (n - c)/64;
Add(res, CHAR_INT(d + 240));
Add(res, CHAR_INT(c + 128));
Add(res, CHAR_INT(b + 128));
Add(res, CHAR_INT(a + 128)); else
return fail; fi;
return res; end; # encode unicode string to GAP string in UTF-8 encoding
UNICODE_RECODE.Encoder.("UTF-8") := function(ustr)
local res, f, n;
res := "";
f := UNICODE_RECODE.UTF8UnicodeChar; for n in IntListUnicodeString(ustr) do
Append(res, f(n));
od;
return res; end; # non-ASCII characters to XML character entities
UNICODE_RECODE.Encoder.("XML") := function(ustr)
local res, n;
res := ""; for n in IntListUnicodeString(ustr) do if n < 128then
Add(res, CHAR_INT(n)); else
Append(res, Concatenation("", LowercaseString(HexStringInt(n)),";")); fi;
od;
return res; end;
UNICODE_RECODE.RFC3986Unreserved := Set(List( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~",
INT_CHAR));
UNICODE_RECODE.RFC3986Reserved := Set(List("!*'();:@&=+$,/?%#[]", INT_CHAR));
UNICODE_RECODE.Encoder.("URL") := function(arg)
local ustr, encreserved, res, s, i, j;
ustr := arg[1]; # also allow UTF-8 GAP string if not IsUnicodeString(ustr) then
ustr := Unicode(ustr); fi; if Length(arg) > 1then
encreserved := arg[2]; else
encreserved := "%"; fi; if IsString(encreserved) then
encreserved := Set(List(encreserved, INT_CHAR)); fi;
res := ""; for i in IntListUnicodeString(ustr) do if i in UNICODE_RECODE.RFC3986Unreserved then
Add(res, CHAR_INT(i)); elif i in UNICODE_RECODE.RFC3986Reserved and not i in encreserved then
Add(res, CHAR_INT(i)); elif i < 128then
Add(res, '%'); if i < 16then
Add(res, '0'); fi;
Append(res, HexStringInt(i)); else
s := Encode(Unicode([i]), "UTF-8"); for j in List(s, INT_CHAR) do
Add(res, '%');
Append(res, HexStringInt(j));
od; fi;
od;
return res; end;
# non-ASCII characters to LaTeX code, if known from LaTeXUnicodeTable # args: unicodestring[, leavemarkup[, leaveutf8]]
UNICODE_RECODE.Encoder.("LaTeX") := function(arg)
local ustr, leavemarkup, tt, res, pos, s, n, leaveutf8, al;
ustr := arg[1]; if Length(arg) > 1then
leavemarkup := arg[2]; else
leavemarkup := false; fi; if Length(arg) > 2then
leaveutf8 := arg[3]; else
leaveutf8 := false; fi;
tt := LaTeXUnicodeTable;
res := ""; for n in IntListUnicodeString(ustr) do
al := Set([ 35, 36, 37, 38, 45, 60, 62, 92, 94, 95, 123, 125, 126]);
pos := Position(al, n); if pos <> fail and not leavemarkup then
Append(res, tt[pos][2]); elif n < 128then
Add(res, CHAR_INT(n)); else
pos := PositionSorted(tt, [n]); if IsBound(tt[pos]) and tt[pos][1] = n then
Append(res, tt[pos][2]); elif leaveutf8 = true then
Append(res, UNICODE_RECODE.UTF8UnicodeChar(n)); else
s := Encode(Unicode([n]), GAPInfo.TermEncoding);
Info(InfoWarning, 1, "#W Missing LaTeX translation of unicode character ",
String(n), ":", s, ",\nadd to LaTeXUnicodeTable\n");
Append(res, Concatenation("Unicode(", String(n), ")")); fi; fi;
od;
return res; end;
UNICODE_RECODE.NormalizedEncodings.LaTeX := "LaTeX";
UNICODE_RECODE.NormalizedEncodings.latex := "LaTeX";
UNICODE_RECODE.NormalizedEncodings.BibTeX := "LaTeX";
InstallGlobalFunction(SimplifiedUnicodeString, function(arg)
local ustr, single, max, tt, res, pos, a, f, n;
ustr := arg[1]; # at most single character substitutions?
single := false; # maximal untouched character (255 for latin1 and 127 for ASCII)
max := 255; if"single" in arg then
single := true; fi; if"ascii" in arg or "ASCII" in arg or "ANSI_X3.4-1968" in arg then
max := 127; fi;
tt := SimplifiedUnicodeTable;
res := []; for n in IntListUnicodeString(ustr) do if n <= max then
Add(res, n); else
pos := PositionSorted(tt, [n]); if IsBound(tt[pos]) and tt[pos][1] = n then
a := tt[pos];
f := Filtered([2..Length(a)], i-> (IsInt(a[i]) and a[i] <= max)
or (IsList(a[i]) and ForAll(a[i], j-> j <= max))); if single then
f := Filtered(f, i-> IsInt(a[i]) or Length(a[i]) <= 1); fi; if Length(f) > 0then
a := a[f[1]]; if IsInt(a) then
Add(res, a); else
Append(res, a); fi; else # ? is '?'
Add(res, 63); fi; else
Add(res, 63); fi; fi;
od;
return Unicode(res); end);
InstallGlobalFunction(LowercaseUnicodeString, function(ustr)
local res, tt, pos, i;
res := ShallowCopy(IntListUnicodeString(ustr));
tt := LowercaseUnicodeTable; for i in [1..Length(res)] do
pos := PositionSorted(tt, [res[i]]); if IsBound(tt[pos]) and tt[pos][1] = res[i] then
res[i] := tt[pos][2]; fi;
od;
return Unicode(res); end);
InstallGlobalFunction(UppercaseUnicodeString, function(ustr)
local res, UppercaseUnicodeTable, tt, pos, i;
res := ShallowCopy(IntListUnicodeString(ustr)); if not IsBound(UppercaseUnicodeTable) then
UppercaseUnicodeTable := Set(List(LowercaseUnicodeTable, a-> [a[2],a[1]])); fi;
tt := UppercaseUnicodeTable; for i in [1..Length(res)] do
pos := PositionSorted(tt, [res[i]]); if IsBound(tt[pos]) and tt[pos][1] = res[i] then
res[i] := tt[pos][2]; fi;
od;
return Unicode(res); end);
# ISO-8859 cases, substitute '?' for unknown characters
UNICODE_RECODE.f := function()
local nam, i; for i in Concatenation([1..11],[13..15]) do
nam := MakeImmutable(Concatenation("ISO-8859-", String(i)));
UNICODE_RECODE.Encoder.(nam) := function(ustr)
local t, s, res, pos, c; if not IsBound(UNICODE_RECODE.TABLES.reverse.(nam)) then
t := [0..255];
s := ShallowCopy(UNICODE_RECODE.TABLES.(nam));
SortParallel(s, t);
UNICODE_RECODE.TABLES.reverse.(nam) := [s, t]; fi;
t := UNICODE_RECODE.TABLES.reverse.(nam);
res := []; for c in IntListUnicodeString(ustr) do if c < 160then
Add(res, c); else
pos := PositionSorted(t[1], c); if pos = fail then
Add(res, 63); # '?' else
Add(res, t[2][pos]); fi; fi;
od;
return STRING_SINTLIST(res); end;
od; end;
UNICODE_RECODE.f();
Unbind(UNICODE_RECODE.f);
UNICODE_RECODE.Encoder.("ANSI_X3.4-1968") := function(ustr)
local res;
res := List(IntListUnicodeString(ustr), function(i) if i < 128then return i; else return 63; fi; end);
return STRING_SINTLIST(res); end;
InstallMethod(Encode, [IsUnicodeString, IsString], function(ustr, enc) if not IsBound(UNICODE_RECODE.NormalizedEncodings.(enc)) then
Error("Sorry, only the following encodings are supported for Encode:\n",
RecNames(UNICODE_RECODE.Encoder), "\n"); fi;
enc := UNICODE_RECODE.NormalizedEncodings.(enc);
return UNICODE_RECODE.Encoder.(enc)(ustr); end); # generic dispatcher for encoding depending on extra data (e.g. used with "URL"
InstallOtherMethod(Encode, [IsUnicodeString, IsString, IsObject], function(ustr, enc, data) if not IsBound(UNICODE_RECODE.NormalizedEncodings.(enc)) then
Error("Sorry, only the following encodings are supported for Encode:\n",
RecNames(UNICODE_RECODE.Encoder), "\n"); fi;
enc := UNICODE_RECODE.NormalizedEncodings.(enc);
return UNICODE_RECODE.Encoder.(enc)(ustr, data); end);
# here the default is UTF-8 encoding
InstallMethod(Encode, [IsUnicodeString], function(ustr)
return UNICODE_RECODE.Encoder.("UTF-8")(ustr); end);
## <#GAPDoc Label="WidthUTF8String"> ## <ManSection > ## <Heading>Lengths of UTF-8 strings</Heading> ## <Func Arg="str" Name="WidthUTF8String" /> ## <Func Arg="str" Name="NrCharsUTF8String" /> ## <Returns>an integer</Returns> ## <Description> ## Let <A>str</A> be a &GAP; string with text in UTF-8 encoding. There are ## three <Q>lengths</Q> of such a string which must be distinguished. The ## operation <Ref BookName="reference" Oper="Length"/> returns the number of ## bytes and so the memory occupied by <A>str</A>. The function <Ref ## Func="NrCharsUTF8String"/> returns the number of unicode characters in ## <A>str</A>, that is the length of <C>Unicode(<A>str</A>)</C>. <P/> ## ## In many applications the function <Ref Func="WidthUTF8String"/> is more ## interesting, it returns the number of columns needed by the string if ## printed to a terminal. This takes into account that some unicode ## characters are combining characters and that there are wide characters ## which need two columns (e.g., for Chinese or Japanese). (To be precise: ## This implementation assumes that there are no control characters in ## <A>str</A> and uses the character width returned by the <C>wcwidth</C> ## function in the GNU C-library called with UTF-8 locale.) ## <Example> ## gap> # A, German umlaut u, B, zero width space, C, newline ## gap> str := Encode( Unicode( "AüB​C\n", "XML" ) );; ## gap> Print(str); ## AüBC ## gap> # umlaut u needs two bytes and the zero width space three ## gap> Length(str); ## 9 ## gap> NrCharsUTF8String(str); ## 6 ## gap> # zero width space and newline don't contribute to width ## gap> WidthUTF8String(str); ## 4 ## </Example> ## </Description> ## </ManSection> ## <#/GAPDoc>
InstallGlobalFunction(NrCharsUTF8String, function(str)
local n, nc, c;
n := 0; for c in str do
nc := INT_CHAR(c); if nc < 128 or nc > 191then
n := n+1; fi;
od;
return n; end);
InstallGlobalFunction(WidthUTF8String, function(str)
local ustr, res, pos, i; if not IsUnicodeString(str) then
ustr := Unicode(str, "UTF-8"); if ustr = fail then
Error("string str is not in UTF-8 encoding.\n"); else
str := ustr; fi; fi;
str := IntListUnicodeString(str);
res := 0; for i in str do if i > 31 and i < 127then
res := res+1; else
pos := PositionSorted(WidthUnicodeTable, [i]); if not IsBound(WidthUnicodeTable[pos]) or WidthUnicodeTable[pos][1] <> i then
pos := pos-1; fi;
res := res + WidthUnicodeTable[pos][2]; fi;
od;
return res; end);
## <#GAPDoc Label="InitialSubstringUTF8String"> ## <ManSection > ## <Func Arg="str, maxwidth[, suf]" Name="InitialSubstringUTF8String" /> ## <Returns>UTF-8 encoded string</Returns> ## <Description> ## The arguments <A>str</A> and <A>suf</A> each must be a &GAP; string with ## text in UTF-8 encoding or a unicode string. The argument <A>suf</A> is ## optional and its default value is the empty string. If the visible width ## of <A>str</A> is at most <A>maxwidth</A> then <A>str</A> is returned as ## UTF-8 encoded &GAP; string. Otherwise, <A>suf</A> is appended to the ## maximal initial substring of <A>str</A> such that the total visible ## width of the result is at most <A>maxwidth</A>. ## ## <Example> ## gap> # A, German umlaut u, B, zero width space, C, newline ## gap> str := Encode( Unicode( "AüB​C\n", "XML" ) );; ## gap> ini := InitialSubstringUTF8String(str, 3);; ## gap> WidthUTF8String(ini); ## 3 ## gap> IntListUnicodeString(Unicode(ini)); ## [ 65, 252, 66, 8203 ] ## gap> l := Unicode([ 23380, 22827, 23376 ] );; # three chars of width 2 ## gap> s := InitialSubstringUTF8String(l, 4, "*");; ## gap> WidthUTF8String(s); ## 3 ## </Example> ## </Description> ## </ManSection> ## <#/GAPDoc>
InstallGlobalFunction(InitialSubstringUTF8String, function(str, len, lsuf...)
local u, suf, wsuf, cut, ints, sum, pos, j; if not IsUnicodeString(str) then
u := Unicode(str, "UTF-8"); if u = fail then
Error("string str is not in UTF-8 encoding"); fi;
ints := IntListUnicodeString(u); else
ints := IntListUnicodeString(str); fi; if Length(lsuf) > 0then if IsUnicodeString(lsuf[1]) then
suf := Encode(lsuf[1], "UTF-8"); else
suf := lsuf[1]; fi; else
suf := ""; fi;
wsuf := WidthUTF8String(suf);
sum := 0;
cut := -1; for j in [1..Length(ints)] do if ints[j] > 31 and ints[j] < 127then
sum := sum + 1; else
pos := PositionSorted(WidthUnicodeTable, [ints[j]]); if not IsBound( WidthUnicodeTable[ pos ] ) or
WidthUnicodeTable[pos][1] <> ints[j] then
pos := pos-1; fi;
sum := sum + WidthUnicodeTable[pos][2]; fi; if sum+wsuf > len and cut < 0then
cut := j-1; fi; if sum > len then
return Concatenation(Encode(Unicode(ints{[1..cut]})), suf); fi;
od; if IsString(str) then
return str; else
return Encode(str, "UTF-8"); fi; end);
# Not (yet?) documented utility to translate a latin1 or UTF-8 encoded # GAP string to a string with lowercase ASCII characters. Can be used for # sorting and searching, allowing some freedom for the input of non-ASCII # characters.
InstallGlobalFunction(LowerASCIIString, function(str)
local u; # heuristic to distinguish UTF-8 and latin1
u := Unicode(str); if u = fail then
u := Unicode(str, "latin1"); fi;
u := SimplifiedUnicodeString(u, "ASCII");
u := LowercaseUnicodeString(u);
return Encode(u); end);
# overwrite library method if sensible depending on term encoding and # encoding of string
InstallMethod(ViewObj, "IsString", true, [IsString and IsFinite],0, function(s)
local u, c;
u := Unicode(s, GAPInfo.TermEncoding); if u <> fail then
Print("\"");
u := IntListUnicodeString(u); for c in u do if c = 34then
Print("\\\""); elif c = 92then
Print("\\\\"); elif c < 32then
Print(SPECIAL_CHARS_VIEW_STRING[2][c+1]); else
Print(Encode(Unicode([c]), GAPInfo.TermEncoding)); fi;
od;
Print("\""); else
PrintObj(s); fi; end);
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.73Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-12)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.