md
Séparateur de liste
2016-07-06

La mise à jour de mon analyseur d'expression mathématique pour l'utiliser avec Free Pascal, m'a amené à enquêter sur le séparateur de liste utilisé dans différents endroits. Alors que Windows contient un symbole dont la valeur est fixée en fonction des paramètres régionaux de l'utilisateur, il n'y en a pas dans Linux. Quel est le séparateur de liste par défaut défini dans une application fonctionnant sur Linux ? La réponse est que le séparateur de liste est la virgule ',' tel qu'aux Etats-Unis et il n'est pas ajusté en fonction des paramètres régionaux sélectionnés par l'utilisateur.

Le wiki Multiplatform Programming Guide, recommande l'ajout de l'unité clocale.pp au début de la liste des unités utilisées dans le fichier du projet comme dans l'exemple suivant :

program project1; {$mode objfpc} uses {$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF} clocale, {$ENDIF} Interfaces, // this includes the LCL widgetset Forms, main { you can add units after this }; {$R *.res} begin RequireDerivedFormResource := True; Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end.

L'unité clocale importe l'unité SysUtils.pas qui contient l'enregistrement DefaultFormatSettings qui est une variable initialisée :

var DefaultFormatSettings : TFormatSettings = ( CurrencyFormat: 1; NegCurrFormat: 5; ThousandSeparator: ','; DecimalSeparator: '.'; CurrencyDecimals: 2; DateSeparator: '-'; TimeSeparator: ':'; ListSeparator: ','; ...

Dans Linux, les valeurs de cet enregistrement ne sont pas modifiées dans le code d'initialisation de l'unité SysUtils. En revanche, l'initialisation de clocale modifie certains champs de l'enregistrement en fonction d'information obtenu du système d'exploitation. Puisque Linux n'a pas de séparateur de liste, ce champs reste inchangé.

En conséquence de ce comportement, les séparateurs décimal et de liste seront tous deux la virgule ',' pour de nombreux utilisateurs. C'est justement le cas sur mon système où le locale est fr_CA. Mon analyseur ne peut pas fonctionner avec ces valeurs. À titre d'exemple, supposons que le locale soit en_UK et considérons l'expression max(1.2,24.54,32.1). Celle-ci retourne 32,1 la plus grande valeur. Mais que sera max(1,2,24,54,32,1) lorsque la virgule est à la fois symbole décimal et séparateur de liste ? Mon analyseur retournera 32,1 parce qu'il cherche à former les plus grands nombres possible. Ainsi 1,2 est traité comme un numéro unique et non pas comme deux entiers séparés. Toutefois, cela est ambigu et l'utilisateur aurait pu attendre 54 ou 54,32 comme réponse. Dans Windows le séparateur de liste pour fr_CA est le point-virgule. L'expression aurait été écrite max(1,2;24,54;32,1) et à nouveau le résultat serait, sans ambiguïté, 32,1.

Pour éviter ce problème, j'ai décidé de modifié le fichier du projet de la façon suivante 

program project1; {$mode objfpc} uses {$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF} clocale, {$ENDIF} Interfaces, // this includes the LCL widgetset Forms, main { you can add units after this }; {$R *.res} begin If DefaultFormatSettings.DecimalSeparator = ‘,’ then DefaultFormatSettings.ListSeparator = ‘;’. RequireDerivedFormResource := True; Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end.

Bien que cette démarche résout mon problème, crée-t-elle des problèmes pour les autres? J'ai étudié cette question dans Windows où il est relativement facile d'obtenir les valeurs des champs des divers locales. Trois symboles sont utilisés comme séparateur décimal : . , / alors que le séparateur de liste est l'un de quatre symboles : , ; ° ? (il n'est pas clair si ? est le bon symbole ou une indication que le symbole est inconnu). Voici les données trouvée dans Windows 10 :

Nombre de locales258
Le séparateur décimal est ‘.’130
Le séparateur décimal est ‘,’127
Le séparateur décimal est ‘/’1
Le séparateur de liste est ‘,’67
Le séparateur de liste est ‘;’188
Le séparateur de liste est ‘°’2
Le séparateur de liste est ‘?’1
Les séparateurs décimal et de liste sont ‘,’ & ‘;’121
Les séparateurs décimal et de liste sont ‘.’ & ‘;’67
Les séparateurs décimal et de liste sont ‘.’ & ‘,’61
Les séparateurs décimal et de liste sont ‘,’ & ‘,’6
- autres combinaisons3

Le code additionnel ajuste correctement le séparateur de liste pour les 121 lieux qui utilisent la virgule comme symbole décimal. En plus, il n'a pas d'impact pour les 130 lieux qui utilisent le point comme séparateur décimal et le seul endroit qui utilise la barre oblique. La correction pourrait être considérée comme une «erreur» pour 6 lieux (Arn_CL, en_ZA, gn_PY, quz_BO, quz_EC, vi_VN) qui utilisent effectivement la virgule à la fois comme séparateur de liste et comme séparateur décimal. Compte tenu de l'ambiguïté d'un tel choix, je pense que l'ajustement proposé est un compromis acceptable.

Données: comme fichier texte, comme fichier XLS (Excel), et comme fichier ODS (LibreOffice.Calc).