Czy isalnum() w C jest zależne od locale'a, a jeśli tak to jak ustalić kodowanie?

0

Pracuję nad swoją biblioteką T-Regx, i próbuję ustalić szereg zasad dla poprawnych delimiterów.

foreach (\range(0, 255) as $byte) {
    $delimiter = chr($byte);
    preg::match($delimiter . 'foo' . $delimiter, 'foo'); // valid or not
}

W PHP, różne delimitery są poprawne dla jednych locale'i, niepoprawne dla innych. Zrobiłem for'a po wszystkich wspieranych locale'ach, i zrobiłem intersekcję, na nich. To jest lista bajtów - przypisane mają true jeśli są poprawne we wszystkich sprawdzanych locale'ach, i false jeśli chociaż w jednym locale'u są niepoprawne. Chciałbym żeby moja biblioteka działała dokładnie tak samo na każdym locale'u, więc muszę znaleźć listę bajtów które są zawsze legalne.

Tutaj jest intersekcja

{"0":false,"1":true,"2":true,"3":true,"4":true,"5":true,"6":true,"7":true,"8":true,"9":false,"10":false,"11":false,"12":false,"13":false,"14":true,"15":true,"16":true,"17":true,"18":true,"19":true,"20":true,"21":true,"22":true,"23":true,"24":true,"25":true,"26":true,"27":true,"28":true,"29":true,"30":true,"31":true,"32":false,"33":true,"34":true,"35":true,"36":true,"37":true,"38":true,"39":true,"40":false,"41":true,"42":true,"43":true,"44":true,"45":true,"46":true,"47":true,"48":false,"49":false,"50":false,"51":false,"52":false,"53":false,"54":false,"55":false,"56":false,"57":false,"58":true,"59":true,"60":false,"61":true,"62":true,"63":true,"64":true,"65":false,"66":false,"67":false,"68":false,"69":false,"70":false,"71":false,"72":false,"73":false,"74":false,"75":false,"76":false,"77":false,"78":false,"79":false,"80":false,"81":false,"82":false,"83":false,"84":false,"85":false,"86":false,"87":false,"88":false,"89":false,"90":false,"91":false,"92":false,"93":true,"94":true,"95":true,"96":true,"97":false,"98":false,"99":false,"100":false,"101":false,"102":false,"103":false,"104":false,"105":false,"106":false,"107":false,"108":false,"109":false,"110":false,"111":false,"112":false,"113":false,"114":false,"115":false,"116":false,"117":false,"118":false,"119":false,"120":false,"121":false,"122":false,"123":false,"124":true,"125":true,"126":true,"127":true,"128":false,"129":false,"130":true,"131":false,"132":true,"133":true,"134":true,"135":true,"136":true,"137":true,"138":false,"139":true,"140":false,"141":false,"142":false,"143":false,"144":false,"145":true,"146":true,"147":true,"148":true,"149":true,"150":true,"151":true,"152":false,"153":true,"154":false,"155":true,"156":false,"157":false,"158":false,"159":false,"160":false,"161":false,"162":false,"163":false,"164":false,"165":false,"166":false,"167":false,"168":false,"169":false,"170":false,"171":false,"172":false,"173":false,"174":false,"175":false,"176":false,"177":false,"178":false,"179":false,"180":false,"181":false,"182":false,"183":false,"184":false,"185":false,"186":false,"187":false,"188":false,"189":false,"190":false,"191":false,"192":false,"193":false,"194":false,"195":false,"196":false,"197":false,"198":false,"199":false,"200":false,"201":false,"202":false,"203":false,"204":false,"205":false,"206":false,"207":false,"208":false,"209":false,"210":false,"211":false,"212":false,"213":false,"214":false,"215":false,"216":false,"217":false,"218":false,"219":false,"220":false,"221":false,"222":false,"223":false,"224":false,"225":false,"226":false,"227":false,"228":false,"229":false,"230":false,"231":false,"232":false,"233":false,"234":false,"235":false,"236":false,"237":false,"238":false,"239":false,"240":false,"241":false,"242":false,"243":false,"244":false,"245":false,"246":false,"247":false,"248":false,"249":false,"250":false,"251":false,"252":false,"253":false,"254":false,"255":false}

Spodziewałem się że ASCII (bajty od 0 do 127) będą się różnić, ale bajty 128 będą zawsze nielegalne. Ale niestety, niektóre są poprawne :o Np 130, 132, 133 :o i inne. Policzyłem je, jest 17 wartości które są poprawnymi delimiterami w zakresie 128-255. Nie mam bladego pojęcia skąd się wzięły.

Próbowałem je porównać do Windows-1250 oraz iso-8859-1, ale nie pasują mi do niczego - tzn code-pointy w tych kodowaniach wydają się zupełnie randomowe. W utf-8 nie ma co szukać, bo single-byte w utf8 to tylko ASCII.

Zacząłem szukać w kodzie źródłowym php, i znalazłem to: https://github.com/php/php-src/blob/master/ext/pcre/php_pcre.c#L656 . To jest if, który sprawdza poprawność delimitera,

if (isalnum((int)*(unsigned char *)&delimiter) || delimiter == '\\') {

Znalazłem tą dokumentację: https://en.cppreference.com/w/cpp/locale/isalnum ale nie potrafię zrozumieć jak różne locale'e wpływają na to czy coś jest isalnum() czy nie. Oraz tak na prawdę, jakiego kodowania spodziewa się ta funkcja.

Tu na przykład jest lista dla polskiego locale'a:

screenshot-20210908144409.png

z tego co rozumiem, to jak zrobiłem intersekcję tych 225 locale'i, to spodziewałbym się że prędzej czy później, każdy bajt z zakresu 128-255 będzie nielegalny w którymś, ale te 17 zostały nielegalne. Nie wiem czy pominąłem jakiś locale? Czy po prostu te bajty są zawsze legalne?

Ktoś ma jakiś pomysł?

3

Linkujesz do isalnum z C++: https://en.cppreference.com/w/cpp/locale/isalnum
a w kodzie masz isalnum z C: https://www.cplusplus.com/reference/cctype/isalnum/ i fajna tabelka stamtąd: https://www.cplusplus.com/reference/cctype/

Zgodnie z https://linux.die.net/man/3/isalpha The details of what characters belong into which class depend on the current locale.

0
Saalin napisał(a):

Linkujesz do isalnum z C++: https://en.cppreference.com/w/cpp/locale/isalnum
a w kodzie masz isalnum z C: https://www.cplusplus.com/reference/cctype/isalnum/

O, faktycznie, dzięki.

i fajna tabelka stamtąd: https://www.cplusplus.com/reference/cctype/

Okej, czytam to

The characters in the extended character set (above 0x7F) may belong to diverse categories depending on the locale and the platform. As a general rule, ispunct, isgraph and isprint return true on these for the standard C locale on most platforms supporting extended character sets.

I faktycznie tu jest info że od 0x7F w górę to czy coś jest alphanum czy nie jest trochę randomowe. @Saalin Jakiś pomysł jak ustalić to?

0

Nie wiem czy zrozumiałem problem, więc nie obraź się jeśli nie na temat :D

Od tego masz mapy locale? Dla isalnum i unixa masz LC_CTYPE, którym sterujesz wyborem locale wpływającego na działanie isalpha.
https://www.gnu.org/software/libc/manual/html_node/Locales.html

Z tego co rozumiem funkcjonalność locale działa w oparciu o mapy. Na boxie, do którego jestem podpięty mam 791 map z definicją locale.

-bash-4.2$ locale -a | wc -l
791
-bash-4.2$ locale -a | head -n 5
aa_DJ
aa_DJ.iso88591
aa_DJ.utf8
aa_ER
aa_ER@saaho

Dla przykładu: en_US mam w /usr/share/i18n/locales/en_US

W definicji, widać, że locale en_US "dziedziczy" z en_GB.

LC_CTYPE
copy "en_GB"
END LC_CTYPE

W /usr/share/i18n/locales/en_GB klas C_TYPE:

C_CTYPE
copy "i18n"

W i18 definicja dla alpha jest dość długa:

LC_CTYPE
% The following is the 14652 i18n fdcc-set LC_CTYPE category.
% It covers Unicode version 5.0.0.
% The character classes and mapping tables were automatically generated
% using the gen-unicode-ctype.c program.

...
% The "alpha" class of the "i18n" FDCC-set is reflecting
% the recommendations in TR 10176 annex A
alpha /
% BASIC LATIN/
   <U0041>..<U005A>;<U0061>..<U007A>;/
% LATIN-1 SUPPLEMENT/
   <U00AA>;<U00B5>;<U00BA>;<U00C0>..<U00D6>;<U00D8>..<U00F6>;/
   <U00F8>..<U00FF>;/
% LATIN EXTENDED-A/
   <U0100>..<U017F>;/
% LATIN EXTENDED-B/
   <U0180>..<U024F>;/
% IPA EXTENSIONS/
   <U0250>..<U02AF>;/
% SPACING MODIFIER LETTERS/
   <U02B0>..<U02C1>;<U02C6>..<U02D1>;<U02E0>..<U02E4>;/
   <U02EE>;/
% COMBINING DIACRITICAL MARKS/
   <U0345>;/
% BASIC GREEK/
   <U0370>..<U0373>;<U0376>..<U0377>;<U037A>..<U037D>;<U0386>;/
   <U0388>..<U038A>;<U038C>;<U038E>..<U03A1>;/
   <U03A3>..<U03CE>;/
% GREEK SYMBOLS AND COPTIC/
   <U03D0>..<U03F5>;<U03F7>..<U03FF>;/
% CYRILLIC/
   <U0400>..<U0481>;<U048A>..<U04FF>;/
% CYRILLIC SUPPLEMENT/
   <U0500>..<U0523>;/
% ARMENIAN/
   <U0531>..<U0556>;<U0559>;<U0561>..<U0587>;/
i leci dalej
...

">0x7F" zależy więc do tego co jest zapisane w konkretnej mapie.

Na innych platformach nie wiem, ale zgadywałbym, że zasada ta sama, tylko implementacja inna.

0
vpiotr napisał(a):

Nie znam sie wiec tylko linkuje:

https://pubs.opengroup.org/onlinepubs/007904875/basedefs/xbd_chap07.html

Dobra, przeczytałem część.

Nadal nie wiem czemu niektóre bajty są alphanum() niezależnie od locale'a :/

0
Saalin napisał(a):

Linkujesz do isalnum z C++: https://en.cppreference.com/w/cpp/locale/isalnum

a w kodzie masz isalnum z C: https://www.cplusplus.com/reference/cctype/isalnum/ i fajna tabelka stamtąd: https://www.cplusplus.com/reference/cctype/

Zgodnie z https://linux.die.net/man/3/isalpha The details of what characters belong into which class depend on the current locale.

A jest gdzieś lista tych locale'i, żeby sobie zobaczyć które litery mogą być raz isalnum(), true a raz false?

1 użytkowników online, w tym zalogowanych: 0, gości: 1