Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2013-04

Unit for handling UTF-8 strings

20 messages · 7 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Kostas Michalopoulos

Hi all,

Yesterday i came up with the need to use some UTF-8 handling (string parts, length, etc). I looked around in FPC 2.6.2's units and found nothing beyond utf8encode/decode (which in linux requires a C widestring manager that i'd like to avoid... and doesn't really help in all cases since Unicode can exceed the 16bit range).

Searching in Google i found a discussion from 2007 which basically concluded to "yeah, it is a nice feature, has some warts, but people need it" but didn't went anywhere
http://free-pascal-general.1045716.n5.nabble.com/UTF-8-versions-of-Copy-and-Length-td2814536.html and
the apparent lack of a UTF8 unit in FPC six years later (even for basic stuff like copy, length, etc) means that that unit never came to exist.

So, what is going on with that? Graeme mentioned that he already had some code and knew some other library that provided a more complete solution that could be imported in FPC and even another guy had yet another library. But still no UTF8 in FPC, despite all the different implementations floating around out there and despite UTF8 being the most important Unicode encoding (being used by practically anything that doesn't falsely believe that 16bit integers are enough).

Personally i coded yet another unit, which you can find here: http://pastebin.com/cJ2TvRdZ

Of course my code is most likely slow and there might be some bugs there - i only did some testing with Greek characters which seem to work fine, but nothing like Chinese or the new emoticon stuff which is regularly added in Unicode.

And since i spent more than i'd like to (~1h, i wasn't exactly proficient with UTF-8 and at some point i got those loops wrong) making a unit that should have been part of Free Pascal since the 90s and my unit might not be exactly the best code for the task out there, i'd like to see this problem solved once and for all. Even if it doesn't provide UpCase/LoCase or whatever else that might be the problem since in this case a partial solution is better than no solution at all because many people are going to only need the partial solution anyway.

So, is there any progress with that? If not, feel free to use the above unit if no other is found suitable (which i doubt since it seems to be a problem that people re-solve all the time because of FPC's lack of an official UTF-8 unit). If any modification, relicensing or whatever is needed i can do it.

Just put some UTF-8 in there so i (and others) won't have to do it again in the future when we want to write code that compiles out of the box in FPC :-P

Kostas "Bad Sector" Michalopoulos

Reply by email

Jonas Maebe
to Kostas Michalopoulos
Kostas Michalopoulos wrote
But still no UTF8 in FPC, despite all the different implementations floating around out there and despite UTF8 being the most important Unicode encoding (being used by practically anything that doesn't falsely believe that 16bit integers are enough).

Why is it that no debate about unicode can ever be held without adding flamebait about either UTF-8 or UTF-16? Please don't react to the above part of that message.

Jonas
FPC mailing lists admin

Reply by email

Kostas Michalopoulos
to Jonas Maebe

Eh, you just did :-P

It wasn't added as a flamebait, i sincerely believe that UTF-8 is the most important encoding since it is used almost everywhere when it comes to Unicode.

Also I didn't mention UTF-16, i mentioned 16bit integers which some people believe that they're enough to represent the whole of unicode and treat each entry in strings of 16bit integers as characters much like they used to treat eachentry in strings of 8bit integers as characters in early codepage-based environments such as DOS and Win9x.

Of course i added the 16bit ints part mostly as a joke in a similar way to saying "main in C really does return int (despite a lot of people for some reason wrongly use void)" since i didn't expect anyone to take that part seriously. I didn't thought much about writing that part to be honest, my interest lies in adding UTF-8 to FPC's RTL units, not what other people think about UTF-16, UCS-2, ASCII, EBCDIC or whatever :-).

Jonas Maebe wrote 22 lines
On 07 Apr 2013, at 13:35, Kostas Michalopoulos wrote: But still no UTF8 in FPC, despite all the different implementations floating around out there and despite UTF8 being the most important Unicode encoding (being used by practically anything that doesn't falsely believe that 16bit integers are enough). Why is it that no debate about unicode can ever be held without adding flamebait about either UTF-8 or UTF-16? Please don't react to the above part of that message. Jonas FPC mailing lists admin _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

Mattias Gaertner
to Kostas Michalopoulos: Yesterday i came up with the need to use some UTF-8 handling (string parts, length, etc).…
[...]I looked around in FPC 2.6.2's units and found nothing beyond utf8encode/decode (which in linux requires a C widestring manager that i'd like to avoid... and doesn't really help in all cases since Unicode can exceed the 16bit range).

It does not require a widestring manager.

Show quoted text 10 lines
Searching in Google i found a discussion from 2007 which basically concluded to "yeah, it is a nice feature, has some warts, but people need it" but didn't went anywhere http://free-pascal-general.1045716.n5.nabble.com/UTF-8-versions-of-Copy-and-Length-td2814536.html and the apparent lack of a UTF8 unit in FPC six years later (even for basic stuff like copy, length, etc) means that that unit never came to exist. So, what is going on with that? Graeme mentioned that he already had some code and knew some other library that provided a more complete solution

See for example the Lazarus lazutf8 unit.

Show quoted text 5 lines
that could be imported in FPC and even another guy had yet another library. But still no UTF8 in FPC, despite all the different implementations floating around out there and despite UTF8 being the most important Unicode encoding (being used by practically anything that doesn't falsely believe that 16bit integers are enough).

AFAIK there are no UTF16 functions either.
Lazarus provides a lazutf16 unit too.

Show quoted text 7 lines
Personally i coded yet another unit, which you can find here: http://pastebin.com/cJ2TvRdZ Of course my code is most likely slow and there might be some bugs there - i only did some testing with Greek characters which seem to work fine, but nothing like Chinese or the new emoticon stuff which is regularly added in Unicode.

I agree, your unit is most likely slow.

[...]

Mattias

Reply by email

Kostas Michalopoulos
to Mattias Gaertner
Mattias Gaertner wrote 12 lines
On Sun, 7 Apr 2013 13:35:40 +0200 Kostas Michalopoulos wrote: >[...]I looked around in FPC 2.6.2's units and found nothing beyond > utf8encode/decode (which in linux requires a C widestring manager that i'd > like to avoid... and doesn't really help in all cases since Unicode can > exceed the 16bit range). It does not require a widestring manager.

The documentation says otherwise:
http://www.freepascal.org/docs-html/rtl/system/utf8encode.html "For this function to work, a widestring manager must be installed."

Show quoted text 5 lines
See for example the Lazarus lazutf8 unit. AFAIK there are no UTF16 functions either. Lazarus provides a lazutf16 unit too.

Is there a reason for those to be in Lazarus only? Can they be moved to FPC?

Reply by email

Michael Van Canneyt
to Kostas Michalopoulos
Kostas Michalopoulos wrote 24 lines
On Sun, Apr 7, 2013 at 5:59 PM, Mattias Gaertner wrote: On Sun, 7 Apr 2013 13:35:40 +0200 Kostas Michalopoulos wrote: >[...]I looked around in FPC 2.6.2's units and found nothing beyond > utf8encode/decode (which in linux requires a C widestring manager that i'd > like to avoid... and doesn't really help in all cases since Unicode can > exceed the 16bit range). It does not require a widestring manager. The documentation says otherwise: http://www.freepascal.org/docs-html/rtl/system/utf8encode.html "For this function to work, a widestring manager must be installed." See for example the Lazarus lazutf8 unit. AFAIK there are no UTF16 functions either. Lazarus provides a lazutf16 unit too. Is there a reason for those to be in Lazarus only? Can they be moved to FPC?

FPC is preparing for a complete unicode solution, with proper language support. At best, these units are a temporary solution.

Michael.

Reply by email

Mattias Gaertner
to Michael Van Canneyt
[...] FPC is preparing for a complete unicode solution, with proper language support.

Great.
I guess, you mean encoded string types.

But even then, FPC should contain UTF-8 and UTF-16 functions.

At best, these units are a temporary solution.

You might be right about the UTF-8 classes like TFileStreamUTF8 (I really hope).
But I doubt that about basic UTF-8/16 functions.

Mattias

Reply by email

Michael Van Canneyt
to Mattias Gaertner
Mattias Gaertner wrote 10 lines
On Sun, 7 Apr 2013 20:18:51 +0200 (CEST) Michael Van Canneyt wrote: > [...] > FPC is preparing for a complete unicode solution, with proper language support. Great. I guess, you mean encoded string types. But even then, FPC should contain UTF-8 and UTF-16 functions.

Why ? The necessary functionality will be implicit in the various calls.

Show quoted text 5 lines
> At best, these units are a temporary solution. You might be right about the UTF-8 classes like TFileStreamUTF8 (I really hope). But I doubt that about basic UTF-8/16 functions.

Such as ?

Michael.

Reply by email

Mattias Gaertner
to Michael Van Canneyt
Show quoted text 16 lines
On Mon, 8 Apr 2013, Mattias Gaertner wrote: > On Sun, 7 Apr 2013 20:18:51 +0200 (CEST) > Michael Van Canneyt wrote: > >> [...] >> FPC is preparing for a complete unicode solution, with proper language support. > > Great. > I guess, you mean encoded string types. > > But even then, FPC should contain UTF-8 and UTF-16 functions. Why ? The necessary functionality will be implicit in the various calls.

What calls?

Show quoted text 7 lines
>> At best, these units are a temporary solution. > > You might be right about the UTF-8 classes like TFileStreamUTF8 (I > really hope). > But I doubt that about basic UTF-8/16 functions. Such as ?

Functions like determining the number of bytes of an UTF-8 character or checking if it is a valid character.

Mattias

Reply by email

Mattias Gaertner
to Michael Schnell
Show quoted text 5 lines
On 04/08/2013 07:02 PM, Mattias Gaertner wrote: > I guess, you mean encoded string types. AFAIK, you can just create string variables of the appropriate coding type and an assignment will do auto-conversion.

Yes.
But how do you examine the characters?
If I understand Michael right, there will be some "implicit functions" for that. I wonder how they work.

Mattias

Reply by email

Michael Van Canneyt
to Mattias Gaertner
Mattias Gaertner wrote 13 lines
On Tue, 09 Apr 2013 08:24:11 +0200 Michael Schnell wrote: > On 04/08/2013 07:02 PM, Mattias Gaertner wrote: >> I guess, you mean encoded string types. > > AFAIK, you can just create string variables of the appropriate coding > type and an assignment will do auto-conversion. Yes. But how do you examine the characters? If I understand Michael right, there will be some "implicit functions" for that. I wonder how they work.

See the character unit:

  // flat functions
   function ConvertFromUtf32(AChar : UCS4Char) : UnicodeString;
   function ConvertToUtf32(const AString : UnicodeString; AIndex : Integer) : UCS4Char; overload;
   function ConvertToUtf32(const AString : UnicodeString; AIndex : Integer; out ACharLength : Integer) : UCS4Char; overload;
   function ConvertToUtf32(const AHighSurrogate, ALowSurrogate : UnicodeChar) : UCS4Char; overload;
   function GetNumericValue(AChar : UnicodeChar) : Double; overload;
   function GetNumericValue(const AString : UnicodeString; AIndex : Integer) : Double; overload;
   function GetUnicodeCategory(AChar : UnicodeChar) : TUnicodeCategory; overload;
   function GetUnicodeCategory(const AString : UnicodeString; AIndex : Integer) : TUnicodeCategory; overload;
   function IsControl(AChar : UnicodeChar) : Boolean; overload;
   function IsControl(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function IsDigit(AChar : UnicodeChar) : Boolean; overload;
   function IsDigit(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function IsSurrogate(AChar : UnicodeChar) : Boolean; overload;
   function IsSurrogate(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function IsHighSurrogate(AChar : UnicodeChar) : Boolean; overload;
   function IsHighSurrogate(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function IsLowSurrogate(AChar : UnicodeChar) : Boolean; overload;
   function IsLowSurrogate(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function IsSurrogatePair(const AHighSurrogate, ALowSurrogate : UnicodeChar) : Boolean; overload;
   function IsSurrogatePair(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function IsLetter(AChar : UnicodeChar) : Boolean; overload;
   function IsLetter(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function IsLetterOrDigit(AChar : UnicodeChar) : Boolean; overload;
   function IsLetterOrDigit(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function IsLower(AChar : UnicodeChar) : Boolean; overload;
   function IsLower(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function IsNumber(AChar : UnicodeChar) : Boolean; overload;
   function IsNumber(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function IsPunctuation(AChar : UnicodeChar) : Boolean; overload;
   function IsPunctuation(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function IsSeparator(AChar : UnicodeChar) : Boolean; overload;
   function IsSeparator(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function IsSymbol(AChar : UnicodeChar) : Boolean; overload;
   function IsSymbol(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function IsUpper(AChar : UnicodeChar) : Boolean; overload;
   function IsUpper(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function IsWhiteSpace(AChar : UnicodeChar) : Boolean; overload;
   function IsWhiteSpace(const AString : UnicodeString; AIndex : Integer) : Boolean; overload;
   function ToLower(AChar : UnicodeChar) : UnicodeChar; overload;
   function ToLower(const AString : UnicodeString) : UnicodeString; overload;
   function ToUpper(AChar : UnicodeChar) : UnicodeChar; overload;
   function ToUpper(const AString : UnicodeString) : UnicodeString; overload;

Reply by email

Mattias Gaertner
to Michael Van Canneyt
Show quoted text 19 lines
On Tue, 9 Apr 2013, Mattias Gaertner wrote: > On Tue, 09 Apr 2013 08:24:11 +0200 > Michael Schnell wrote: > >> On 04/08/2013 07:02 PM, Mattias Gaertner wrote: >>> I guess, you mean encoded string types. >> >> AFAIK, you can just create string variables of the appropriate coding >> type and an assignment will do auto-conversion. > > Yes. > But how do you examine the characters? > If I understand Michael right, there will be some "implicit functions" > for that. I wonder how they work. See the character unit:

Nice!

Why do you call them "implicit calls"?

Will there be UTF-8 functions too or do you have to convert to UnicodeString?

Will there be PUnicodeChar functions too?

Show quoted text 6 lines
// flat functions function ConvertFromUtf32(AChar : UCS4Char) : UnicodeString; function ConvertToUtf32(const AString : UnicodeString; AIndex : Integer) : UCS4Char; overload; function ConvertToUtf32(const AString : UnicodeString; AIndex : Integer; out ACharLength : Integer) : UCS4Char; overload; function ConvertToUtf32(const AHighSurrogate, ALowSurrogate : UnicodeChar) : UCS4Char; overload; [...]

Mattias

Reply by email

Paul Ishenin
to Mattias Gaertner
Mattias Gaertner wrote
Will there be UTF-8 functions too or do you have to convert to UnicodeString?

At the moment TCharacter contains methods which delphi TCharacter has. If there is demand we will add UTF8 overloads.

Show quoted text 7 lines
Will there be PUnicodeChar functions too? > // flat functions > function ConvertFromUtf32(AChar : UCS4Char) : UnicodeString; > function ConvertToUtf32(const AString : UnicodeString; AIndex : Integer) : UCS4Char; overload; > function ConvertToUtf32(const AString : UnicodeString; AIndex : Integer; out ACharLength : Integer) : UCS4Char; overload; > function ConvertToUtf32(const AHighSurrogate, ALowSurrogate : UnicodeChar) : UCS4Char; overload;

Best regards,
Paul Ishenin

Reply by email

Michael Schnell
to Mattias Gaertner: On Tue, 09 Apr 2013 08:24:11 +0200 Yes. But how do you examine the characters? If I…
Mattias Gaertner wrote
But how do you examine the characters?

Even defining what a character is, is extremely problematic with any use of Unicode. Regarding that a "printable character" can be assembled by multiple of the (nearly 2^32) Unicode "codes", and a single Unicode codes is represented by 1, 2, 3, or 4 Bytes when using UTF-8 or UTF-16 encoding, and now the order of those bytes depends on the CPU-arch and/or the file the string is imported from and the way it is imported. This of course is not a problem introduced by fpc, but the perfectly normal complexity of Unicode.

If I understand Michael right, there will be some "implicit functions" for that. I wonder how they work.

This is what Delphi compatibility dictated. (You might read the Delphi XE Docs on how to code Unicode enabled Delphi source.)

I do hope, fpc avoids some of the quirks Delphi introduces and offers some useful additional features (e.g. dedicated string types such as unencoded (raw, never auto-converted) Byte, Word and DWord Strings, and a "flexible encoded" String type, that inherit the encoding scheme from the source string when doing an assignment or using them as a function parameter, doing auto-conversion whenever dynamically necessary.

-Michael

Reply by email

Mattias Gaertner
to Paul Ishenin: At the moment TCharacter contains methods which delphi TCharacter has. If there is demand…
Show quoted text 10 lines
Paul Ishenin hat am 9. April 2013 um 09:20 geschrieben: 09.04.2013 15:13, Mattias Gaertner wrote: > Will there be UTF-8 functions too or do you have to convert > to UnicodeString? At the moment TCharacter contains methods which delphi TCharacter has. If there is demand we will add UTF8 overloads.

Demand+=1

> Will there be PUnicodeChar functions too?

Well?

Mattias

Reply by email

Sven Barth
to Mattias Gaertner
Mattias Gaertner wrote 10 lines
> Paul Ishenin hat am 9. April 2013 um 09:20 geschrieben: > > > 09.04.2013 15:13, Mattias Gaertner wrote: > >> Will there be UTF-8 functions too or do you have to convert >> to UnicodeString? > At the moment TCharacter contains methods which delphi TCharacter has. > If there is demand we will add UTF8 overloads. Demand+=1
(1,8) Error: Illegal expression
(1,9) Error: Illegal expression
(1,9) Fatal: Syntax error, ";" expected but "ordinal const" found

(Sorry, had to be said :P )

Regards,
Sven

Reply by email

Paul Ishenin
to Sven Barth

09.04.2013 17:10, Sven Barth пишет:

Show quoted text 6 lines
> Demand+=1 (1,8) Error: Illegal expression (1,9) Error: Illegal expression (1,9) Fatal: Syntax error, ";" expected but "ordinal const" found (Sorry, had to be said :P )

Also, Patches variable seems to be equal to zero. And assigning Demand without assigning Patches has almost no effect :)

Best regards,
Paul Ishenin

Reply by email

Mattias Gaertner
to Paul Ishenin
Show quoted text 14 lines
Paul Ishenin hat am 9. April 2013 um 11:23 geschrieben: 09.04.2013 17:10, Sven Barth пишет: >> Demand+=1 > (1,8) Error: Illegal expression > (1,9) Error: Illegal expression > (1,9) Fatal: Syntax error, ";" expected but "ordinal const" found > > (Sorry, had to be said :P ) Also, Patches variable seems to be equal to zero. And assigning Demand without assigning Patches has almost no effect :)

Creating a patch is not hard. The lazutf8 already contains the code. But I have no idea how the the interface should look like. TCharacter is a Delphi class and Delphi does not have UTF-8 functions. Michael wrote that these functions are "implicit", so maybe these functions need to fit some form? In other words:
Are there any suggestions, recommendations how the UTF-8 functions should look like?

Mattias

Reply by email

Paul Ishenin
to Mattias Gaertner

09.04.2013 18:09, Mattias Gaertner пишет:

Show quoted text 7 lines
Creating a patch is not hard. The lazutf8 already contains the code. But I have no idea how the the interface should look like. TCharacter is a Delphi class and Delphi does not have UTF-8 functions. Michael wrote that these functions are "implicit", so maybe these functions need to fit some form? In other words: Are there any suggestions, recommendations how the UTF-8 functions should look like?

Let's see.

The next function should stay as is. Compiler will add required implicit conversion when you assign result to UTF8String variable.

function ConvertFromUtf32(AChar : UCS4Char) : UnicodeString;

Here you can add UTF8String overloads if needed:

function ConvertToUtf32(const AString : UnicodeString; AIndex : Integer)
: UCS4Char; overload;
function ConvertToUtf32(const AString : UnicodeString; AIndex : Integer;
out ACharLength : Integer) : UCS4Char; overload;

At the same time even without UTF8 overloads compiler will insert implicit conversion from UTF8String to UnicodeString when you pass it to that functions. So UTF8 overloads can only increase spead by removing 1 implicit conversion.

Best regards,
Paul Ishenin

Reply by email