Beat: Unicode Sortierung

Hallo

Eins vorab. Ich bin mir bewusst, dass ohne Locale es keine geeignete Sortierung geben kann.
Jedoch bin ich wohl nicht der einzige, der ein locale nicht immer für die geeignete Lösung hält. Wer eine mehrsprachige Webseite publiziert, ist gleich gezwungen, für jede Sprache ein locale umzuschalten. Einerseits ist dies wohl nicht wahnsinnig performant, anderseits befriedigt dies auch nicht Situationen, in welchen Artikel aus mehreren Sprachen gleichzeitig verarbeitet werden müssen.

Deshalb meine Frage die im Kontext eines Sortieralgorythmus steht.

Ich kann viele Kombinuierte Zeichen auf ein Alphabetisches Zeichen aus A-Z zurückführen.
Dabei gilt jedoch zu beachten:
éêè sind erweiterungen von e und nach e zu sortieren.
ö ist ein Umlaut und ist damit eine Variante die nach oz, aber vor p einzuordnen ist.
ebenso das Smörebröd Ø ist wie ö einzuordnen.
ß ist nach ss aber vor st einzuordnen.

Ich habe also zwei Klassen von erweiterten Wurzelbuchstaben, ohne aber innerhalb der Klasse noch eine weitere Rangstufe zu definieren.

Meine Frage nun.
Mit welchem Mechanismus kann ich (in Perl oder allgemein) einen Wurzelbuchstaben zu einem Kombinierten Buchstaben ermitteln, um für den sortkey eine s/// Ersetzung im Sinne neuer Gewichtung vornehmen zu können?

Mir schwebt vor, dass im sortkey die Ersetzung vorgenommen wird:
a => a0
à => a1
ä => a2
Von den ursprünglichen Buchstaben sollen also eine Wurzel aus [a-zA-Z] plus eine Klassenziffer übrigbleiben.

werden sollte. Damit das möglich ist, muss ich auch via Unicode einen Unterschied zwischen â und ä definieren.
In Perl kann ich selber Unicode Properties definieren. Das würde dann halt eine lange Liste.
Aber auch hier die Frage: Gibt es zwei Eigenschaften, nach welchen ich ä von ã unterscheiden kann, oder muss ich die erst selber definieren.

PS: Die Problemstellung besteht für einen Sortieralgorithmus. Es würde mir jedoch an dieser Stelle ein fertiges Unicode Sort paket wenig nützen, da ich zusätzlich ein natural sort implementiere.

mfg Beat

--
   <°)))o><                     ><o(((°>o
Der Valigator leibt diese Fische
  1. Moin!

    Deshalb meine Frage die im Kontext eines Sortieralgorythmus steht.

    Ich kann viele Kombinuierte Zeichen auf ein Alphabetisches Zeichen aus A-Z zurückführen.
    Dabei gilt jedoch zu beachten:
    éêè sind erweiterungen von e und nach e zu sortieren.
    ö ist ein Umlaut und ist damit eine Variante die nach oz, aber vor p einzuordnen ist.
    ebenso das Smörebröd Ø ist wie ö einzuordnen.
    ß ist nach ss aber vor st einzuordnen.

    Das sind alles falsche Aussagen. Jedenfalls wenn man die passende Sprache wählt.

    Das ist ja doch gerade der Punkt: Jede Sprache hat ihr eigenes Alphabet und ihre eigene Sortierung. Das kriegst du nicht Universalisiert - außer du sortiert ganz banal nach Unicode-Codepoint.

    - Sven Rautenberg

    1. Hallo,

      éêè sind erweiterungen von e und nach e zu sortieren.
      ö ist ein Umlaut und ist damit eine Variante die nach oz, aber vor p einzuordnen ist.
      ebenso das Smörebröd Ø ist wie ö einzuordnen.
      ß ist nach ss aber vor st einzuordnen.
      Das sind alles falsche Aussagen.

      ich hatte es eher als Wunsch bzw. Forderung von Beat aufgefasst.

      außer du sortiert ganz banal nach Unicode-Codepoint.

      Und genau das würde ich auf meinen Rechnern (sowohl Windows als auch Linux) gern als Default-Sortierung haben - insbesondere möchte ich nicht, dass einige Zeichen wie z.B. das Minuszeichen oder Klammern bei der Sortierung ganz ignoriert werden. Bisher habe ich aber keinen Ansatzpunkt gefunden, wie ich den Systemen das beibringe.

      So long,
       Martin

      --
      Die letzten Worte des Neandertalers:
      Möchte doch zu gern wissen, was in der Höhle ist ...
      1. éêè sind erweiterungen von e und nach e zu sortieren.
        ö ist ein Umlaut und ist damit eine Variante die nach oz, aber vor p einzuordnen ist.
        ebenso das Smörebröd Ø ist wie ö einzuordnen.
        ß ist nach ss aber vor st einzuordnen.
        Das sind alles falsche Aussagen.

        ich hatte es eher als Wunsch bzw. Forderung von Beat aufgefasst.

        Richtig ein soll, wenn auch an diesem Punkt noch nicht durchdacht. Das braucht im Detail viel mehr Recherche.

        außer du sortiert ganz banal nach Unicode-Codepoint.

        Und genau das würde ich auf meinen Rechnern (sowohl Windows als auch Linux) gern als Default-Sortierung haben - insbesondere möchte ich nicht, dass einige Zeichen wie z.B. das Minuszeichen oder Klammern bei der Sortierung ganz ignoriert werden. Bisher habe ich aber keinen Ansatzpunkt gefunden, wie ich den Systemen das beibringe.

        Ich denke, das was ich brauche, ist die Umkehrung der Normalisieerung

        ein ö kannst du durch ein o und ein ¨ erzeugen. Ein Editor normalisiert on the fly. ¨ + o ergeben ö . Das ist als normalisierung bekannt.
        Wenn ich nun die Normalisierung Rückgängig machen kann, dann kann ich den ersten teil einem Wurzelbuchstaben zuordnen, oder schlicht p{alpha}, und die Erweiterung einer Klasse zuordnen.

        mfg Beat

        --
        ><o(((°>           ><o(((°>
           <°)))o><                     ><o(((°>o
        Der Valigator leibt diese Fische
        1. ein ö kannst du durch ein o und ein ¨ erzeugen. Ein Editor normalisiert on the fly. ¨ + o ergeben ö . Das ist als normalisierung bekannt.

          Jain. ¨ ist ein combining character – die verbinden sich automatisch mit dem vorhergehenden Unicode-Charakter, das ist noch nicht wirklich Normalisierung auf Charakterbasis, da setzen sich einfach zwei Zeichen zu einem Glyph zusammen. Es sei denn der Editor verändert auch seinen Speicher.

          Wenn ich nun die Normalisierung Rückgängig machen kann, dann kann ich den ersten teil einem Wurzelbuchstaben zuordnen

          Unicode definiert vier Normalisierungsalgorithmen, NFC, NFD, NFKC und NFKD. Bei den C-Formen werden kombinierende Zeichenfolgen zu einem Zeichen zusammengefasst, wenn es dafür ein passendes Zeichen ist. Die D-Formen dekomponieren dagegen, danach bist Du auf der Suche. Du musst noch entscheiden, ob Du eventuell die K-Form haben möchtest, die normalisiert manche Zeichen auch nach definierter Kompabilität, die mehr eine Ähnlichkeit ist, z.B. eine ² zu einer 2. Ich kann mir vorstellen, dass das für einen Sortieralgorithmus sowohl praktisch als auch unpraktisch sein kann.

          Für Perl gibt es bestimmt eine Implementierung für NF(K)D, ansonsten ist der in Anhang 15 definierte Algorithmus kein Hexenwerk. Ich hab den mal in JS für einen ähnlichen Fall wie den Deinen implementiert, um Akzente zu normalisieren. Das nervigste sind die Tabellen, die man sich aus den Unicode Character Database rausfischen muss.

          1. Wenn ich nun die Normalisierung Rückgängig machen kann, dann kann ich den ersten teil einem Wurzelbuchstaben zuordnen

            Unicode definiert vier Normalisierungsalgorithmen, NFC, NFD, NFKC und NFKD.

            C Composing
            D Decomposing

            Bei den C-Formen werden kombinierende Zeichenfolgen zu einem Zeichen zusammengefasst, wenn es dafür ein passendes Zeichen ist. Die D-Formen dekomponieren dagegen, danach bist Du auf der Suche. Du musst noch entscheiden, ob Du eventuell die K-Form haben möchtest, die normalisiert manche Zeichen auch nach definierter Kompabilität, die mehr eine Ähnlichkeit ist, z.B. eine ² zu einer 2. Ich kann mir vorstellen, dass das für einen Sortieralgorithmus sowohl praktisch als auch unpraktisch sein kann.

            Es ist vielleicht auch eine Frage der Datenmenge, welche Granulierung in der Sortierung überhaupt effektiv ist.
            Mein Ziel ist auf jeden Fall, binäre sortierkey zu erstellen, damit die sortierung dann mit einem einfach cmp stattfinden kann.

            Für Perl gibt es bestimmt eine Implementierung für NF(K)D, ansonsten ist der in Anhang 15 definierte Algorithmus kein Hexenwerk. Ich hab den mal in JS für einen ähnlichen Fall wie den Deinen implementiert, um Akzente zu normalisieren. Das nervigste sind die Tabellen, die man sich aus den Unicode Character Database rausfischen muss.

            Eben das habe ich befürchtet, dass ich da selbst Basteln muss.
            Immerhin weiss ich jetzt, wonach ich suchen muss und dass ich mit meinem Anliegen nicht der erste bin.

            Ach, ich möchte noch nachführen: Ein grund, warum ich nicht mit locale arbeiten möchte:
            Ein locale kann ein Script in einen unsicheren Zustand versetzen, wenn man es nicht auf eng begrenzte Blocks anwendet. Ich sehe auch nicht wie man es mit -T sicher kombinieren kann. Und das locale kann manchmal auch gar nicht verändert werden. Aus diesem Grund hat ein Unicode basierter Ansatz hier seine Berechtigung.

            mfg Beat

            --
            ><o(((°>           ><o(((°>
               <°)))o><                     ><o(((°>o
            Der Valigator leibt diese Fische
            1. Hi,

              Ach, ich möchte noch nachführen: Ein grund, warum ich nicht mit locale arbeiten möchte:
              Ein locale kann ein Script in einen unsicheren Zustand versetzen, wenn man es nicht auf eng begrenzte Blocks anwendet.

              Das kommt wohl stark auf die Umgebung an, in der das Script läuft.

              Und das locale kann manchmal auch gar nicht verändert werden. Aus diesem Grund hat ein Unicode basierter Ansatz hier seine Berechtigung.

              Und definierte Mindestanforderungen für die Lauffähigkeit des Scriptes mit garantierten Ergebnissen haben da ihre Berechtigung, wo man andernfalls etwas, was es eigentlich schon in brauchbarer Form gibt, selber noch mal unter hohem Aufwand nachprogrammieren muss.

              MfG ChrisB

              --
              Light travels faster than sound - that's why most people appear bright until you hear them speak.
            2. Eben das habe ich befürchtet, dass ich da selbst Basteln muss.

              Unicode::Normalize scheint doch das gewünschte zu machen.

              Was ich noch vergaß zu erwähnen: NFKD reicht natürlich nicht aus, weil z.B. „ß“ davon nicht betroffen ist. Ich würde also in der Vergleichsfunktion noch auf Uppercase normalisieren. Wenn ich denn selber eine teure Vergleichsfunktion basteln wollen würde.

              Der gleiche Autor des obigen Moduls hat nämlich noch Unicode::Collate geschrieben, dass den Unicode Collation Algorithm zumindest größtenteils implementiert. Das kann man sich zunutze machen, vor allem, weil man eine eigene Collation-Tabelle angeben kann.

              1. Eben das habe ich befürchtet, dass ich da selbst Basteln muss.

                Unicode::Normalize scheint doch das gewünschte zu machen.

                Habe ich heute bereits verwendet. Bringt bereits gute Resultate.
                Allerdings scheint meine unicodeData.txt nicht auf dem aktuellsten Stand zu sein.
                Ich verwende einen Testfont Junicode. Ich stelle fest dass für viele hier umgesetzte Codepunkte noch kein Eintrag in Perls UnicodeData.txt besteht.
                Na dann halt zum Update via ppm.

                Was ich noch vergaß zu erwähnen: NFKD reicht natürlich nicht aus, weil z.B. „ß“ davon nicht betroffen ist. Ich würde also in der Vergleichsfunktion noch auf Uppercase normalisieren. Wenn ich denn selber eine teure Vergleichsfunktion basteln wollen würde.

                Case ist noch ein Offener Punkt.
                Die meisten locales sortieren AaBb.
                Komischerweise sortiert mein Algorithmus mit locale aAbB.
                Ich möchte meiner Sortierfunktion aber eine Property mitgeben, welche Basiessortierungen definiert:
                NAaBb NABab ABabN AaBbN

                Ich habe mir heute mal einen Unicode Viewer gebastelt, der mir die Informationen via Unicode::UCD ausgibt.

                Dekomposition hilft für die meisten Fälle die ich will. Ebenso wie die vernächlässigung nach NFKD von Zeichen die weder \p{L} noch \p{N} sind

                Beispiel:
                NFKC($string))
                'NFKD'
                ...
                Note
                "\N{LATIN
                (Oct
                ...
                maybe
                $may_be_composed_with_prev_char
                modification;

                Der gleiche Autor des obigen Moduls hat nämlich noch Unicode::Collate geschrieben, dass den Unicode Collation Algorithm zumindest größtenteils implementiert. Das kann man sich zunutze machen, vor allem, weil man eine eigene Collation-Tabelle angeben kann.

                Damit könnte ich Æ, ß, æ, IJ, ij etc... sortieren. Die Anzahl scheint mir überschaubar, vor allem wenn NFKD vorher durchgeführt wird.

                Bis jetzt haben ich auf jeden Fall mal Einblicke in Unicode gewonnen, und die Ideen für den Sortieralgorithmus nehmen gestalt an.

                mfg Beat

                --
                ><o(((°>           ><o(((°>
                   <°)))o><                     ><o(((°>o
                Der Valigator leibt diese Fische
                1. Anbei ein cleiner Unicode Viewer für den Webbetrieb.
                  Er zeigt über das Modul Unicode::UCD einige Informationen an, die in einer Charmap sonst fehlen.

                  Code steht zur allgemeinen Verfügung.

                    
                    
                  #!perl -C7  
                  use warnings;  
                  use strict;  
                  use constant { NL => "\n"};  
                    
                  BEGIN {  
                  	use CGI::Carp qw(carpout fatalsToBrowser);  
                  	open(LOG, ">","error.txt")  or  die "Unable to append to error.txt: $!\n";  
                  	carpout(*LOG);  
                  }  
                    
                  use Unicode::CharName qw(uname ublock);  
                  use Unicode::UCD 'charinfo';  
                    
                  my %User = (  
                  	blockstart=>0,  
                  	blockend=>1,  
                  );  
                    
                  for( $ENV{QUERY_STRING} ){  
                  	/blockstart=(\d+)/ and $User{blockstart} = $1;  
                  	/blockend=(\d+)/ and $User{blockend} = $1;  
                  }  
                    
                  print "Content-type: text/html; charset=utf-8\n\n",  
                  	q(<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"  
                     "http://www.w3.org/TR/html4/strict.dtd">),  
                  	'<html><head>',  
                  	'<style>  
                  	body{font-size:16px; font-face: "DejaVu Sans", Junicode, Arial, sans-serif;}  
                  	table{ margin:0em; table-layout;fixed; border-collapse:collapse;}  
                  	th {font-size:18px; border:1px solid #bbb; background:#eee;}  
                  	td {font-size:20px; line-height:1.5; width:2em; text-align:center; border:1px solid #bbb;}  
                  	td i {display:none; position:fixed; top:0; right:0; width:20em; background:#eee;  padding: 6px;  
                  	font-size:14px; font-style:normal; text-align:left;}  
                  	td i b {float:left; font-size:60px; width:2em;}  
                  	td:hover i {display:block;}  
                  	',NL,  
                  	'</style>',NL,  
                  	'</head><body>',NL,  
                  	'<form action="" method="get">',NL,  
                  	'Block von <input type="text" name="blockstart" value="',($User{blockstart}),'">',NL,  
                  	' bis <input type="text" name="blockend" value="',($User{blockend}),'">',NL,  
                  	'<input type="submit" value=" Abfrage "></form>',NL,  
                  	'<h1>Unicode Codepoints</h1>';  
                  for my $x ( $User{blockstart}..$User{blockend} ){  
                  	#next if $x =~/^(8|15|27|171|21[6-9]|22[0-3])$/;  
                  	print '<h2>',$x,'</h2>',NL;  
                  	print "<table>";  
                  	print "<tr><th>", sprintf("%x",$x * 256) ,"</th>";  
                  	for ( 0..15 ){  
                  		printf("<th>%x</th>",$_);  
                  	}  
                  	print '</tr>';  
                  	for my $y ( 0..15 ){  
                  		printf( "<tr><th>%x</th>", $x * 256 + $y * 16);  
                  		for my $z ( 0..15 ){  
                  			my $c = $x * 256 + $y * 16 + $z;  
                  			my $ci = charinfo($c);  
                  			#print '<td title="U+',sprintf("%x",$c),'; ', uname($c),'; ', ublock($c),'">',  
                  			print '<td>', chr( $c ),  
                  				'<i><b>',chr( $c ),'</b>Nr: U+', sprintf("%x",$c),  
                  				'<br>cat: ', $ci->{category},  
                  				'<br>comb: ', $ci->{combining},  
                  				'<br>decomp: ', $ci->{decomposition},  
                  				'<br>script: ', $ci->{script},  
                  				'<br>', $ci->{name},  
                  				'</i></td>';  
                  		}  
                  		print "</tr>";  
                  	}  
                  	print "</table>",NL;  
                  }  
                    
                  print "</body></html>";  
                    
                  exit;  
                    
                  
                  

                  mfg Beat

                  --
                  ><o(((°>           ><o(((°>
                     <°)))o><                     ><o(((°>o
                  Der Valigator leibt diese Fische
      2. Hi!

        außer du sortiert ganz banal nach Unicode-Codepoint.
        Und genau das würde ich auf meinen Rechnern (sowohl Windows als auch Linux) gern als Default-Sortierung haben

        Für Linux solltest du dann das betreffende Programm mit der locale-Einstellung C laufen lassen.

        Lo!

        1. Hallo,

          außer du sortiert ganz banal nach Unicode-Codepoint.
          Und genau das würde ich auf meinen Rechnern (sowohl Windows als auch Linux) gern als Default-Sortierung haben
          Für Linux solltest du dann das betreffende Programm mit der locale-Einstellung C laufen lassen.

          das ist alles? - Das ist ja einfach ...
          Nee, ehrlich: Ich habe jede Menge Seiten im Web gefunden, die mich über die verfügbaren Einstellungen für die locales informieren, unter anderem auch regelmäßig "C". Aber nirgends wurde erklärt, was die Einstellungen jeweils *bedeuten*, welche Auswirkungen sie haben.

          Danke schonmal; Windows bleibt dann immer noch eine ungelöste Baustelle.

          So long,
           Martin

          --
          Verliebt:    Er spricht, sie lauscht.
          Verlobt:     Sie spricht, er lauscht.
          Verheiratet: Beide sprechen, und die Nachbarn lauschen.
          1. Für Linux solltest du dann das betreffende Programm mit der locale-Einstellung C laufen lassen.

            das ist alles? - Das ist ja einfach ...
            Nee, ehrlich: Ich habe jede Menge Seiten im Web gefunden, die mich über die verfügbaren Einstellungen für die locales informieren, unter anderem auch regelmäßig "C". Aber nirgends wurde erklärt, was die Einstellungen jeweils *bedeuten*, welche Auswirkungen sie haben.

            in Perl unter anderem: \w = [A-Za-z0-9_]

            mfg Beat

            --
            ><o(((°>           ><o(((°>
               <°)))o><                     ><o(((°>o
            Der Valigator leibt diese Fische
    2. @@Sven Rautenberg:

      nuqneH

      Das ist ja doch gerade der Punkt: Jede Sprache hat ihr eigenes Alphabet und ihre eigene Sortierung.

      Wenn’s denn so einfach wäre.

      Das Deutsche kennt verschiedene Varianten der Sortierung.

      Im Serbischen wird je nach Schrift unterschiedlich sortiert.

      Qapla'

      --
      Alle Menschen sind klug. Die einen vorher, die anderen nachher. (John Steinbeck)