Πώς η ταξινόμηση του Linux ταξινομεί τις συμβολοσειρές

Εισαγωγή

Όλα ξεκίνησαν με ένα σύντομο σενάριο που υποτίθεται ότι συνδύαζε πληροφορίες διεύθυνσης e-mail υπαλλήλων που λαμβάνονται από τη λίστα των χρηστών της λίστας αλληλογραφίας, με τις θέσεις εργαζομένων που λαμβάνονται από τη βάση δεδομένων του τμήματος HR. Και οι δύο λίστες εξήχθησαν σε αρχεία κειμένου Unicode UTF-8 και αποθηκεύτηκε με καταλήξεις γραμμών Unix.

Περιεχόμενο mail.txt

Иванов Андрей;[email protected]

Περιεχόμενο buhg.txt

Иванова Алла;маляр
Ёлкина Элла;крановщица
Иванов Андрей;слесарь
Абаканов Михаил;маляр

Για τη συγχώνευση, τα αρχεία ταξινομήθηκαν με την εντολή Unix είδος και υποβάλλονται στην είσοδο του προγράμματος Unix ενταχθούν, το οποίο απέτυχε απροσδόκητα με ένα σφάλμα:

$> sort buhg.txt > buhg.srt
$> sort mail.txt > mail.srt
$> join buhg.srt mail.srt > result
join: buhg.srt:4: is not sorted: Иванов Андрей;слесарь

Η προβολή του αποτελέσματος της ταξινόμησης με τα μάτια σας έδειξε ότι, γενικά, η ταξινόμηση είναι σωστή, αλλά σε περίπτωση συμπτώσεων ανδρικών και γυναικείων επωνύμων, τα θηλυκά προηγούνται των αρσενικών:

$> sort buhg.txt
Абаканов Михаил;маляр
Ёлкина Элла;крановщица
Иванова Алла;маляр
Иванов Андрей;слесарь

Μοιάζει με σφάλμα ταξινόμησης στο Unicode ή σαν εκδήλωση φεμινισμού στον αλγόριθμο ταξινόμησης. Το πρώτο είναι, φυσικά, πιο εύλογο.

Ας το αναβάλουμε προς το παρόν ενταχθούν και επικεντρωθείτε σε είδος. Ας προσπαθήσουμε να λύσουμε το πρόβλημα χρησιμοποιώντας επιστημονικό poking. Αρχικά, ας αλλάξουμε την τοπική ρύθμιση από en_US επί ru_ru. Για την ταξινόμηση, θα αρκούσε να ορίσετε τη μεταβλητή περιβάλλοντος LC_COLLATE, αλλά δεν θα χάσουμε χρόνο σε μικροπράγματα:

$> LANG=ru_RU.UTF-8 sort buhg.txt
Абаканов Михаил;маляр
Ёлкина Элла;крановщица
Иванова Алла;маляр
Иванов Андрей;слесарь

Τίποτα δεν άλλαξε.

Ας προσπαθήσουμε να επανακωδικοποιήσουμε τα αρχεία σε κωδικοποίηση ενός byte:

$> iconv -f UTF-8 -t KOI8-R buhg.txt 
 | LANG=ru_RU.KOI8-R sort 
 | iconv -f KOI8-R -t UTF8

Και πάλι τίποτα δεν έχει αλλάξει.

Δεν μπορείτε να κάνετε τίποτα, θα πρέπει να αναζητήσετε μια λύση στο Διαδίκτυο. Δεν υπάρχει τίποτα άμεσα για τα ρωσικά επώνυμα, αλλά υπάρχουν ερωτήσεις σχετικά με άλλες παραξενιές ταξινόμησης. Για παράδειγμα, εδώ υπάρχει ένα πρόβλημα: Η ταξινόμηση unix αντιμετωπίζει τους χαρακτήρες '-' (παύλα) ως αόρατους. Εν ολίγοις, οι συμβολοσειρές "a-b", "aa", "ac" ταξινομούνται ως "aa", "a-b", "ac".

Η απάντηση είναι τυπική παντού: χρησιμοποιήστε την τοπική ρύθμιση προγραμματιστή "C" και θα είσαι ευτυχισμένος. Ας δοκιμάσουμε:

$> LANG=C sort buhg.txt
Ёлкина Элла;крановщица
Абаканов Михаил;маляр
Иванов Андрей;слесарь
Иванова Алла;адвокат

Κάτι έχει αλλάξει. Οι Ιβάνοφ παρατάχθηκαν με τη σωστή σειρά, αν και η Γιολκίνα κάπου γλίστρησε. Ας επιστρέψουμε στο αρχικό πρόβλημα:

$> LANG=C sort buhg.txt > buhg.srt
$> LANG=C sort mail.txt > mail.srt
$> LANG=C join buhg.srt mail.srt > result

Λειτουργούσε χωρίς σφάλματα, όπως υποσχέθηκε το Διαδίκτυο. Και αυτό παρά την Yolkina στην πρώτη γραμμή.

Το πρόβλημα φαίνεται να έχει λυθεί, αλλά για κάθε περίπτωση, ας δοκιμάσουμε μια άλλη ρωσική κωδικοποίηση - τα Windows CP1251:

$> iconv -f UTF-8 -t CP1251 buhg.txt 
 | LANG=ru_RU.CP1251 sort 
 | iconv -f CP1251 -t UTF8 

Το αποτέλεσμα ταξινόμησης, παραδόξως, θα συμπίπτει με την τοπική ρύθμιση "C", και ολόκληρο το παράδειγμα, κατά συνέπεια, εκτελείται χωρίς σφάλματα. Κάποιο είδος μυστικισμού.

Δεν μου αρέσει ο μυστικισμός στον προγραμματισμό γιατί συνήθως κρύβει λάθη. Θα πρέπει να εξετάσουμε σοβαρά πώς λειτουργεί. είδος και τι επηρεάζει; LC_COLLATE .

Στο τέλος θα προσπαθήσω να απαντήσω στις ερωτήσεις:

  • γιατί τα γυναικεία επώνυμα ταξινομήθηκαν λανθασμένα;
  • γιατί LANG=ru_RU.CP1251 αποδείχθηκε ισοδύναμο LANG=C
  • Γιατί να κάνετε είδος и ενταχθούν διαφορετικές ιδέες για τη σειρά των ταξινομημένων χορδών
  • γιατί υπάρχουν λάθη σε όλα τα παραδείγματά μου;
  • τέλος πώς να ταξινομήσετε τις χορδές σύμφωνα με τις προτιμήσεις σας

Ταξινόμηση σε Unicode

Η πρώτη στάση θα είναι η Τεχνική Έκθεση Νο. 10 με τίτλο Αλγόριθμος ταξινόμησης Unicode σε απευθείας σύνδεση unicode.org. Η έκθεση περιέχει πολλές τεχνικές λεπτομέρειες, οπότε επιτρέψτε μου να κάνω μια σύντομη περίληψη των βασικών ιδεών.

Αντιπαραβολή — Η «σύγκριση» συμβολοσειρών είναι η βάση οποιουδήποτε αλγορίθμου ταξινόμησης. Οι ίδιοι οι αλγόριθμοι μπορεί να διαφέρουν ("φούσκα", "συγχώνευση", "γρήγορος"), αλλά όλοι θα χρησιμοποιούν μια σύγκριση ενός ζεύγους συμβολοσειρών για να καθορίσουν τη σειρά με την οποία εμφανίζονται.

Η ταξινόμηση των χορδών στη φυσική γλώσσα είναι ένα αρκετά περίπλοκο πρόβλημα. Ακόμη και στις πιο απλές κωδικοποιήσεις ενός byte, η σειρά των γραμμάτων στο αλφάβητο, ακόμη και κατά κάποιο τρόπο διαφορετική από το αγγλικό λατινικό αλφάβητο, δεν θα συμπίπτει πλέον με τη σειρά των αριθμητικών τιμών με τις οποίες κωδικοποιούνται αυτά τα γράμματα. Στο γερμανικό αλφάβητο λοιπόν το γράμμα Ö στέκεται ανάμεσα О и P, και στην κωδικοποίηση CP850 μπαίνει ανάμεσα ÿ и Ü.

Μπορείτε να προσπαθήσετε να κάνετε αφαίρεση από μια συγκεκριμένη κωδικοποίηση και να εξετάσετε τα «ιδανικά» γράμματα που είναι ταξινομημένα με κάποια σειρά, όπως γίνεται στο Unicode. Κωδικοποιήσεις UTF8, UTF16 ή ενός byte ΚΟΙ8-Ρ (εάν απαιτείται περιορισμένο υποσύνολο Unicode) θα δώσει διαφορετικές αριθμητικές αναπαραστάσεις γραμμάτων, αλλά θα αναφέρεται στα ίδια στοιχεία του βασικού πίνακα.

Αποδεικνύεται ότι ακόμα κι αν φτιάξουμε έναν πίνακα συμβόλων από την αρχή, δεν θα μπορούμε να του εκχωρήσουμε μια καθολική σειρά συμβόλων. Σε διαφορετικά εθνικά αλφάβητα που χρησιμοποιούν τα ίδια γράμματα, η σειρά αυτών των γραμμάτων μπορεί να διαφέρει. Για παράδειγμα, στα γαλλικά Æ θα θεωρηθεί απολίνωση και θα ταξινομηθεί ως χορδή AE. Στα νορβηγικά Æ θα είναι ένα ξεχωριστό γράμμα, το οποίο βρίσκεται μετά Z. Με την ευκαιρία, εκτός από απολινώσεις όπως Æ Υπάρχουν γράμματα γραμμένα με πολλά σύμβολα. Έτσι στο τσέχικο αλφάβητο υπάρχει ένα γράμμα Ch, που βρίσκεται ανάμεσα H и I.

Εκτός από τις διαφορές στα αλφάβητα, υπάρχουν και άλλες εθνικές παραδόσεις που επηρεάζουν την ταξινόμηση. Συγκεκριμένα, τίθεται το ερώτημα: με ποια σειρά πρέπει να εμφανίζονται στο λεξικό λέξεις που αποτελούνται από κεφαλαία και πεζά γράμματα; Η ταξινόμηση μπορεί επίσης να επηρεαστεί από τη χρήση σημείων στίξης. Στα ισπανικά, ένα ανεστραμμένο ερωτηματικό χρησιμοποιείται στην αρχή μιας ερωτηματικής πρότασης (Σας αρέσει η μουσική?). Σε αυτήν την περίπτωση, είναι προφανές ότι οι ερωτηματικές προτάσεις δεν πρέπει να ομαδοποιούνται σε ξεχωριστό σύμπλεγμα εκτός του αλφαβήτου, αλλά πώς να ταξινομήσετε τις γραμμές με άλλα σημεία στίξης;

Δεν θα επιμείνω στην ταξινόμηση χορδών σε γλώσσες πολύ διαφορετικές από τις ευρωπαϊκές. Σημειώστε ότι σε γλώσσες με κατεύθυνση γραφής από δεξιά προς τα αριστερά ή από πάνω προς τα κάτω, οι χαρακτήρες στις γραμμές είναι πιθανότατα αποθηκευμένοι με σειρά ανάγνωσης και ακόμη και τα μη αλφαβητικά συστήματα γραφής έχουν τους δικούς τους τρόπους να ταξινομούν τις γραμμές χαρακτήρα προς χαρακτήρα . Για παράδειγμα, τα ιερογλυφικά μπορούν να ταξινομηθούν κατά στυλ (Κλειδιά κινεζικών χαρακτήρων) ή με προφορά. Για να είμαι ειλικρινής, δεν έχω ιδέα πώς πρέπει να τακτοποιούνται τα emoji, αλλά μπορείτε να βρείτε κάτι και για αυτά.

Με βάση τα χαρακτηριστικά που αναφέρονται παραπάνω, διατυπώθηκαν οι βασικές απαιτήσεις για τη σύγκριση συμβολοσειρών που βασίζονται σε πίνακες Unicode:

  • Η σύγκριση των συμβολοσειρών δεν εξαρτάται από τη θέση των χαρακτήρων στον πίνακα κωδικών.
  • ακολουθίες χαρακτήρων που σχηματίζουν έναν μόνο χαρακτήρα μειώνονται σε κανονική μορφή (A + ο επάνω κύκλος είναι ίδιος με Å);
  • Κατά τη σύγκριση συμβολοσειρών, ένας χαρακτήρας λαμβάνεται υπόψη στο πλαίσιο της συμβολοσειράς και, εάν είναι απαραίτητο, συνδυάζεται με τους γείτονές του σε μια μονάδα σύγκρισης (Ch στα τσεχικά) ή χωρίζεται σε πολλά (Æ στα γαλλικά);
  • όλα τα εθνικά χαρακτηριστικά (αλφάβητο, κεφαλαία/πεζά, σημεία στίξης, σειρά τύπων γραφής) πρέπει να διαμορφωθούν μέχρι τη μη αυτόματη ανάθεση της σειράς (emoji).
  • Η σύγκριση είναι σημαντική όχι μόνο για την ταξινόμηση, αλλά και σε πολλά άλλα μέρη, για παράδειγμα για τον καθορισμό σειρών σειρών (αντικατάσταση του {A... z} σε βίαιο χτύπημα);
  • η σύγκριση πρέπει να γίνει αρκετά γρήγορα.

Επιπλέον, οι συντάκτες της αναφοράς διατύπωσαν ιδιότητες σύγκρισης στις οποίες δεν πρέπει να βασίζονται οι προγραμματιστές αλγορίθμων:

  • ο αλγόριθμος σύγκρισης δεν πρέπει να απαιτεί ξεχωριστό σύνολο χαρακτήρων για κάθε γλώσσα (η ρωσική και η ουκρανική γλώσσα μοιράζονται τους περισσότερους κυριλλικούς χαρακτήρες).
  • η σύγκριση δεν πρέπει να βασίζεται στη σειρά των χαρακτήρων στους πίνακες Unicode.
  • Το βάρος της συμβολοσειράς δεν πρέπει να είναι χαρακτηριστικό της συμβολοσειράς, καθώς η ίδια συμβολοσειρά σε διαφορετικά πολιτισμικά περιβάλλοντα μπορεί να έχει διαφορετικά βάρη.
  • Τα βάρη σειρών μπορούν να αλλάξουν κατά τη συγχώνευση ή το διαχωρισμό (από x < y δεν ακολουθεί αυτό xz < yz);
  • διαφορετικές συμβολοσειρές που έχουν τα ίδια βάρη θεωρούνται ίσες από την άποψη του αλγορίθμου ταξινόμησης. Η εισαγωγή πρόσθετης σειράς τέτοιων χορδών είναι δυνατή, αλλά μπορεί να υποβαθμίσει την απόδοση.
  • Κατά την επαναλαμβανόμενη ταξινόμηση, οι σειρές με τα ίδια βάρη μπορούν να αλλάξουν. Η ευρωστία είναι μια ιδιότητα ενός συγκεκριμένου αλγορίθμου ταξινόμησης και όχι μια ιδιότητα ενός αλγορίθμου σύγκρισης συμβολοσειρών (δείτε την προηγούμενη παράγραφο).
  • Οι κανόνες ταξινόμησης μπορεί να αλλάξουν με την πάροδο του χρόνου καθώς οι πολιτιστικές παραδόσεις τελειοποιούνται/αλλάζουν.

Ορίζεται επίσης ότι ο αλγόριθμος σύγκρισης δεν γνωρίζει τίποτα για τη σημασιολογία των υπό επεξεργασία συμβολοσειρών. Έτσι, οι συμβολοσειρές που αποτελούνται μόνο από ψηφία δεν πρέπει να συγκρίνονται ως αριθμοί και σε λίστες αγγλικών ονομάτων το άρθρο (Beatles, The).

Προκειμένου να ικανοποιηθούν όλες οι καθορισμένες απαιτήσεις, προτείνεται ένας αλγόριθμος ταξινόμησης πινάκων πολλαπλών επιπέδων (στην πραγματικότητα τεσσάρων επιπέδων).

Προηγουμένως, οι χαρακτήρες στη συμβολοσειρά μειώνονται σε κανονική μορφή και ομαδοποιούνται σε μονάδες σύγκρισης. Σε κάθε μονάδα σύγκρισης εκχωρούνται πολλά βάρη που αντιστοιχούν σε διάφορα επίπεδα σύγκρισης. Τα βάρη των μονάδων σύγκρισης είναι στοιχεία διατεταγμένων συνόλων (στην περίπτωση αυτή, ακεραίων) που μπορούν να συγκριθούν για περισσότερο ή λιγότερο. Ιδιαίτερο νόημα ΑΓΝΟΗΘΗΚΕ (0x0) σημαίνει ότι στο αντίστοιχο επίπεδο σύγκρισης αυτή η μονάδα δεν εμπλέκεται στη σύγκριση. Η σύγκριση των χορδών μπορεί να επαναληφθεί πολλές φορές, χρησιμοποιώντας τα βάρη των αντίστοιχων επιπέδων. Σε κάθε επίπεδο, τα βάρη των μονάδων σύγκρισης δύο σειρών συγκρίνονται διαδοχικά μεταξύ τους.

Σε διαφορετικές υλοποιήσεις του αλγορίθμου για διαφορετικές εθνικές παραδόσεις, οι τιμές των συντελεστών μπορεί να διαφέρουν, αλλά το πρότυπο Unicode περιλαμβάνει έναν βασικό πίνακα βαρών - "Προεπιλεγμένος πίνακας στοιχείων συλλογής Unicode" (DUCET). Θα ήθελα να σημειώσω ότι η ρύθμιση της μεταβλητής LC_COLLATE είναι στην πραγματικότητα μια ένδειξη της επιλογής του πίνακα βαρών στη συνάρτηση σύγκρισης συμβολοσειρών.

Συντελεστές στάθμισης DUCET τακτοποιημένα ως εξής:

  • Στο πρώτο επίπεδο, όλα τα γράμματα μειώνονται στην ίδια περίπτωση, τα διακριτικά απορρίπτονται, τα σημεία στίξης (όχι όλα) αγνοούνται.
  • στο δεύτερο επίπεδο λαμβάνονται υπόψη μόνο οι διακριτικές.
  • στο τρίτο επίπεδο, λαμβάνεται υπόψη μόνο η περίπτωση.
  • στο τέταρτο επίπεδο λαμβάνονται υπόψη μόνο τα σημεία στίξης.

Η σύγκριση πραγματοποιείται σε πολλά περάσματα: πρώτον, συγκρίνονται οι συντελεστές του πρώτου επιπέδου. εάν τα βάρη συμπίπτουν, τότε πραγματοποιείται επαναλαμβανόμενη σύγκριση με τα βάρη δεύτερου επιπέδου. τότε ίσως ένα τρίτο και ένα τέταρτο.

Η σύγκριση τελειώνει όταν οι σειρές περιέχουν αντίστοιχες μονάδες σύγκρισης με διαφορετικά βάρη. Οι σειρές που έχουν ίσα βάρη και στα τέσσερα επίπεδα θεωρούνται ίσες μεταξύ τους.

Αυτός ο αλγόριθμος (με ένα σωρό πρόσθετες τεχνικές λεπτομέρειες) έδωσε το όνομα στην αναφορά Νο. 10 - "Αλγόριθμος Συλλογή Unicode" (ACU).

Εδώ η συμπεριφορά ταξινόμησης από το παράδειγμά μας γίνεται λίγο πιο ξεκάθαρη. Θα ήταν ωραίο να το συγκρίνουμε με το πρότυπο Unicode.

Για δοκιμή υλοποιήσεων ACU υπάρχει ένα ειδικό δοκιμή, χρησιμοποιώντας αρχείο βαρών, υλοποίηση DUCET. Μπορείτε να βρείτε κάθε λογής αστεία πράγματα στο αρχείο της ζυγαριάς. Για παράδειγμα, υπάρχει η σειρά των mahjong και των ευρωπαϊκών ντόμινο, καθώς και η σειρά των κοστουμιών σε μια τράπουλα (σύμβολο 1F000 και επιπλέον). Οι φόρμες τοποθετούνται σύμφωνα με τους κανόνες του bridge - PCBT, και οι κάρτες στη στολή είναι με τη σειρά T, 2,3, XNUMX... K.

Ελέγχοντας με μη αυτόματο τρόπο ότι οι σειρές ταξινομούνται σωστά σύμφωνα με DUCET θα ήταν αρκετά κουραστικό, αλλά, ευτυχώς για εμάς, υπάρχει μια υποδειγματική υλοποίηση της βιβλιοθήκης για εργασία με Unicode - "Διεθνή εξαρτήματα για Unicode"(ΜΕΘ).

Στον ιστότοπο αυτής της βιβλιοθήκης, που αναπτύχθηκε στο IBM, υπάρχουν σελίδες επίδειξης, συμπεριλαμβανομένων σελίδα αλγορίθμου σύγκρισης συμβολοσειρών. Εισάγουμε τις γραμμές δοκιμών μας με προεπιλεγμένες ρυθμίσεις και, ιδού, έχουμε τέλεια ρωσική ταξινόμηση.

Абаканов Михаил;маляр
Ёлкина Элла;крановщица
Иванов Андрей;слесарь
Иванова Алла;адвокат

Παρεμπιπτόντως, η ιστοσελίδα ΜΕΘ Μπορείτε να βρείτε μια διευκρίνιση του αλγόριθμου σύγκρισης κατά την επεξεργασία των σημείων στίξης. Σε παραδείγματα Συχνές ερωτήσεις συλλογής η απόστροφη και η παύλα αγνοούνται.

Το Unicode μας βοήθησε, αλλά ψάξτε για λόγους περίεργης συμπεριφοράς είδος в Linux θα πρέπει να πάει κάπου αλλού.

Ταξινόμηση σε glibc

Γρήγορη προβολή των πηγαίων κωδικών του βοηθητικού προγράμματος είδος του GNU Core Utils έδειξε ότι στο ίδιο το βοηθητικό πρόγραμμα, η τοπική προσαρμογή καταλήγει στην εκτύπωση της τρέχουσας τιμής της μεταβλητής LC_COLLATE όταν εκτελείται σε λειτουργία εντοπισμού σφαλμάτων:

$ sort --debug buhg.txt > buhg.srt
sort: using ‘en_US.UTF8’ sorting rules

Οι συγκρίσεις συμβολοσειρών πραγματοποιούνται χρησιμοποιώντας την τυπική συνάρτηση strcoll, που σημαίνει ότι όλα τα ενδιαφέροντα βρίσκονται στη βιβλιοθήκη glibc.

Επί wiki σχέδιο glibc αφιερωμένο στη σύγκριση χορδών μια παράγραφος. Από αυτή την παράγραφο μπορεί να γίνει κατανοητό ότι στο glibc Η ταξινόμηση βασίζεται σε έναν αλγόριθμο ήδη γνωστό σε εμάς ACU (Ο αλγόριθμος ταξινόμησης Unicode) και/ή σε ένα πρότυπο κοντά σε αυτό ISO 14651 (Διεθνής παραγγελία και σύγκριση χορδών). Όσον αφορά το πιο πρόσφατο πρότυπο, θα πρέπει να σημειωθεί ότι στον ιστότοπο standards.iso.org ISO 14651 δηλώνεται επίσημα διαθέσιμο στο κοινό, αλλά ο αντίστοιχος σύνδεσμος οδηγεί σε ανύπαρκτη σελίδα. Η Google επιστρέφει αρκετές σελίδες με συνδέσμους σε επίσημους ιστότοπους που προσφέρουν την αγορά ενός ηλεκτρονικού αντιγράφου του προτύπου για εκατό ευρώ, αλλά στην τρίτη ή τέταρτη σελίδα των αποτελεσμάτων αναζήτησης υπάρχουν επίσης απευθείας σύνδεσμοι προς PDF. Γενικά, το πρότυπο πρακτικά δεν διαφέρει από αυτό ACU, αλλά είναι πιο βαρετό στην ανάγνωση γιατί δεν περιέχει σαφή παραδείγματα εθνικών χαρακτηριστικών ταξινόμησης χορδών.

Οι πιο ενδιαφέρουσες πληροφορίες για wiki υπήρχε σύνδεσμος προς ανιχνευτής σφαλμάτων με μια συζήτηση για την εφαρμογή της σύγκρισης συμβολοσειρών στο glibc. Από τη συζήτηση μπορεί να μαθευτεί ότι glibc χρησιμοποιείται για τη σύγκριση χορδών ISOπροσωπικό τραπέζι Ο κοινός πίνακας προτύπων (CTT), τη διεύθυνση του οποίου μπορείτε να βρείτε στην εφαρμογή A το πρότυπο ISO 14651. Μεταξύ 2000 και 2015, αυτός ο πίνακας glibc δεν είχε συντηρητή και ήταν αρκετά διαφορετικό (τουλάχιστον εξωτερικά) από την τρέχουσα έκδοση του προτύπου. Από το 2015 έως το 2018, πραγματοποιήθηκε προσαρμογή στη νέα έκδοση του πίνακα και τώρα έχετε την ευκαιρία να γνωρίσετε στην πραγματική ζωή μια νέα έκδοση του πίνακα (8 CentOS), και παλιά (7 CentOS).

Τώρα που έχουμε όλες τις πληροφορίες σχετικά με τον αλγόριθμο και τους βοηθητικούς πίνακες, μπορούμε να επιστρέψουμε στο αρχικό πρόβλημα και να κατανοήσουμε πώς να ταξινομήσουμε σωστά τις συμβολοσειρές στη ρωσική γλώσσα.

ISO 14651 / 14652

Πηγαίος κώδικας του πίνακα που μας ενδιαφέρει CTT στις περισσότερες διανομές Linux βρίσκεται στον κατάλογο /usr/share/i18n/locales/. Ο ίδιος ο πίνακας βρίσκεται στο αρχείο iso14651_t1_common. Τότε αυτή είναι η οδηγία αρχείων αντιγράψτε το iso14651_t1_common περιλαμβάνονται στο αρχείο iso14651_t1, το οποίο, με τη σειρά του, περιλαμβάνεται σε εθνικά αρχεία, συμπεριλαμβανομένων en_US и ru_ru. Στις περισσότερες διανομές Linux Όλα τα αρχεία προέλευσης περιλαμβάνονται στη βασική εγκατάσταση, αλλά εάν δεν υπάρχουν, θα πρέπει να εγκαταστήσετε ένα πρόσθετο πακέτο από τη διανομή.

Δομή αρχείου iso14651_t1 μπορεί να φαίνεται τρομερά σύνθετο, με μη προφανείς κανόνες για την κατασκευή ονομάτων, αλλά αν το δεις, όλα είναι αρκετά απλά. Η δομή περιγράφεται στο πρότυπο ISO 14652, αντίγραφο του οποίου μπορείτε να κατεβάσετε από τον ιστότοπο open-std.org. Μπορείτε να διαβάσετε μια άλλη περιγραφή της μορφής αρχείου Προδιαγραφές POSIX από OpenGroup. Ως εναλλακτική στην ανάγνωση του προτύπου, μπορείτε να μελετήσετε τον πηγαίο κώδικα της συνάρτησης collate_read в glibc/locale/programs/ld-collate.c.

Η δομή του αρχείου μοιάζει με αυτό:

Από προεπιλογή, ο χαρακτήρας χρησιμοποιείται ως χαρακτήρας διαφυγής και το τέλος της γραμμής μετά τον χαρακτήρα # είναι ένα σχόλιο. Και τα δύο σύμβολα μπορούν να επαναπροσδιοριστούν, κάτι που γίνεται στη νέα έκδοση του πίνακα:

escape_char /
comment_char %

Το αρχείο θα περιέχει διακριτικά στη μορφή ή (Οπου x - δεκαεξαδικό ψηφίο). Αυτή είναι η δεκαεξαδική αναπαράσταση των σημείων κωδικού Unicode στην κωδικοποίηση UCS-4 (UTF-32). Όλα τα άλλα στοιχεία σε γωνιακούς βραχίονες (συμπεριλαμβανομένων , <2> και τα παρόμοια) θεωρούνται απλές σταθερές συμβολοσειράς που έχουν μικρή σημασία εκτός πλαισίου.

Σειρά LC_COLLATE μας λέει ότι στη συνέχεια ξεκινούν τα δεδομένα που περιγράφουν τη σύγκριση των συμβολοσειρών.

Πρώτον, καθορίζονται ονόματα για τα βάρη στον πίνακα σύγκρισης και ονόματα για τους συνδυασμούς συμβόλων. Σε γενικές γραμμές, οι δύο τύποι ονομάτων ανήκουν σε δύο διαφορετικές οντότητες, αλλά στο πραγματικό αρχείο αναμειγνύονται. Τα ονόματα των βαρών καθορίζονται από τη λέξη-κλειδί συρραφή-σύμβολο (σύγκριση χαρακτήρα) γιατί κατά τη σύγκριση, οι χαρακτήρες Unicode που έχουν τα ίδια βάρη θα θεωρούνται ισοδύναμοι χαρακτήρες.

Το συνολικό μήκος της ενότητας στην τρέχουσα αναθεώρηση αρχείου είναι περίπου 900 γραμμές. Τράβηξα παραδείγματα από διάφορα μέρη για να δείξω την αυθαιρεσία των ονομάτων και αρκετούς τύπους σύνταξης.

LC_COLLATE

collating-symbol <RES-1>
collating-symbol <BLK>
collating-symbol <MIN>
collating-symbol <WIDE>
...
collating-symbol <ARABIC>
collating-symbol <ETHPC>
collating-symbol <OSMANYA>
...
collating-symbol <S1D000>..<S1D35F>
collating-symbol <SFFFF> % Guaranteed largest symbol value. Keep at end of this list
...
collating-element <U0413_0301> from "<U0413><U0301>"
collating-element <U0413_0341> from "<U0413><U0341>"

  • συρραφή-σύμβολο καταγράφει μια συμβολοσειρά ΟΣΜΑΝΙΑ στον πίνακα ονομάτων ζυγαριών
  • συρραφή-σύμβολο .. καταχωρεί μια ακολουθία ονομάτων που αποτελείται από ένα πρόθεμα S και δεκαεξαδικό αριθμητικό επίθημα από 1D000 να 1D35F.
  • Ffff в colating-symbol μοιάζει με μεγάλο ανυπόγραφο ακέραιο σε δεκαεξαδικό, αλλά είναι απλώς ένα όνομα που μπορεί να μοιάζει
  • Όνομα σημαίνει σημείο κώδικα στην κωδικοποίηση UCS-4
  • collating-element από "" καταχωρεί ένα νέο όνομα για ένα ζεύγος κουκκίδων Unicode.

Μόλις καθοριστούν τα ονόματα των βαρών, καθορίζονται τα πραγματικά βάρη. Δεδομένου ότι μόνο οι περισσότερες από λιγότερες σχέσεις έχουν σημασία σε σύγκριση, τα βάρη καθορίζονται από μια απλή ακολουθία ονομάτων λίστας. Τα «ελαφρύτερα» βάρη αναφέρονται πρώτα και μετά τα «βαρύτερα». Επιτρέψτε μου να σας υπενθυμίσω ότι σε κάθε χαρακτήρα Unicode εκχωρούνται τέσσερα διαφορετικά βάρη. Εδώ συνδυάζονται σε μια ενιαία διατεταγμένη ακολουθία. Θεωρητικά, οποιοδήποτε συμβολικό όνομα θα μπορούσε να χρησιμοποιηθεί σε οποιοδήποτε από τα τέσσερα επίπεδα, αλλά τα σχόλια δείχνουν ότι οι προγραμματιστές διαχωρίζουν νοητικά τα ονόματα σε επίπεδα.

% Symbolic weight assignments

% Third-level weight assignments
<RES-1>
<BLK>
<MIN>
<WIDE>
...
% Second-level weight assignments
<BASE>
<LOWLINE> % COMBINING LOW LINE
<PSILI> % COMBINING COMMA ABOVE
<DASIA> % COMBINING REVERSED COMMA ABOVE
...
% First-level weight assignments
<S0009> % HORIZONTAL TABULATION 
<S000A> % LINE FEED
<S000B> % VERTICAL TABULATION
...
<S0434> % CYRILLIC SMALL LETTER DE
<S0501> % CYRILLIC SMALL LETTER KOMI DE
<S0452> % CYRILLIC SMALL LETTER DJE
<S0503> % CYRILLIC SMALL LETTER KOMI DJE
<S0453> % CYRILLIC SMALL LETTER GJE
<S0499> % CYRILLIC SMALL LETTER ZE WITH DESCENDER
<S0435> % CYRILLIC SMALL LETTER IE
<S04D7> % CYRILLIC SMALL LETTER IE WITH BREVE
<S0454> % CYRILLIC SMALL LETTER UKRAINIAN IE
<S0436> % CYRILLIC SMALL LETTER ZHE

Τέλος, ο πίνακας πραγματικών βαρών.

Η ενότητα βαρών περικλείεται σε γραμμές λέξεων-κλειδιών παραγγελία_έναρξη и παραγγελία_τέλος. Επιπλέον επιλογές παραγγελία_έναρξη προσδιορίστε σε ποια κατεύθυνση σαρώνονται οι σειρές σε κάθε επίπεδο σύγκρισης. Η προεπιλεγμένη ρύθμιση είναι προς τα εμπρός. Το σώμα της ενότητας αποτελείται από γραμμές που περιέχουν τον κωδικό συμβόλου και τα τέσσερα βάρη του. Ο κωδικός χαρακτήρα μπορεί να αναπαρασταθεί από τον ίδιο τον χαρακτήρα, ένα σημείο κωδικού ή ένα συμβολικό όνομα που ορίστηκε προηγουμένως. Βάρη μπορούν επίσης να δοθούν σε συμβολικά ονόματα, σημεία κωδικού ή τα ίδια τα σύμβολα. Εάν χρησιμοποιούνται σημεία κώδικα ή χαρακτήρες, το βάρος τους είναι το ίδιο με την αριθμητική τιμή του σημείου κώδικα (θέση στον πίνακα Unicode). Οι χαρακτήρες που δεν προσδιορίζονται ρητά (όπως καταλαβαίνω) θεωρούνται ότι έχουν εκχωρηθεί στον πίνακα με κύριο βάρος που ταιριάζει με τη θέση στον πίνακα Unicode. Ειδική τιμή βάρους ΑΓΝΟΩ σημαίνει ότι το σύμβολο αγνοείται στο κατάλληλο επίπεδο σύγκρισης.

Για να δείξω τη δομή της ζυγαριάς, επέλεξα τρία αρκετά προφανή θραύσματα:

  • χαρακτήρες που αγνοούνται εντελώς
  • σύμβολα ισοδύναμα με τον αριθμό τρία στα δύο πρώτα επίπεδα
  • η αρχή του κυριλλικού αλφαβήτου, που δεν περιέχει διακριτικά, και επομένως ταξινομείται κυρίως κατά το πρώτο και το τρίτο επίπεδο.

order_start forward;forward;forward;forward,position
<U0000> IGNORE;IGNORE;IGNORE;IGNORE % NULL (in 6429)
<U0001> IGNORE;IGNORE;IGNORE;IGNORE % START OF HEADING (in 6429)
<U0002> IGNORE;IGNORE;IGNORE;IGNORE % START OF TEXT (in 6429)
...
<U0033> <S0033>;<BASE>;<MIN>;<U0033> % DIGIT THREE
<UFF13> <S0033>;<BASE>;<WIDE>;<UFF13> % FULLWIDTH DIGIT THREE
<U2476> <S0033>;<BASE>;<COMPAT>;<U2476> % PARENTHESIZED DIGIT THREE
<U248A> <S0033>;<BASE>;<COMPAT>;<U248A> % DIGIT THREE FULL STOP
<U1D7D1> <S0033>;<BASE>;<FONT>;<U1D7D1> % MATHEMATICAL BOLD DIGIT THREE
...
<U0430> <S0430>;<BASE>;<MIN>;<U0430> % CYRILLIC SMALL LETTER A
<U0410> <S0430>;<BASE>;<CAP>;<U0410> % CYRILLIC CAPITAL LETTER A
<U04D1> <S04D1>;<BASE>;<MIN>;<U04D1> % CYRILLIC SMALL LETTER A WITH BREVE
<U0430_0306> <S04D1>;<BASE>;<MIN>;<U04D1> % CYRILLIC SMALL LETTER A WITH BREVE
...
<U0431> <S0431>;<BASE>;<MIN>;<U0431> % CYRILLIC SMALL LETTER BE
<U0411> <S0431>;<BASE>;<CAP>;<U0411> % CYRILLIC CAPITAL LETTER BE
<U0432> <S0432>;<BASE>;<MIN>;<U0432> % CYRILLIC SMALL LETTER VE
<U0412> <S0432>;<BASE>;<CAP>;<U0412> % CYRILLIC CAPITAL LETTER VE
...
order_end

Τώρα μπορείτε να επιστρέψετε στην ταξινόμηση των παραδειγμάτων από την αρχή του άρθρου. Η ενέδρα βρίσκεται σε αυτό το μέρος του πίνακα βαρών:

<U0020> IGNORE;IGNORE;IGNORE;<U0020> % SPACE
<U0021> IGNORE;IGNORE;IGNORE;<U0021> % EXCLAMATION MARK
<U0022> IGNORE;IGNORE;IGNORE;<U0022> % QUOTATION MARK
...

Φαίνεται ότι σε αυτόν τον πίνακα τα σημεία στίξης από τον πίνακα ASCII (συμπεριλαμβανομένου του διαστήματος) σχεδόν πάντα αγνοείται κατά τη σύγκριση χορδών. Οι μόνες εξαιρέσεις είναι οι γραμμές που ταιριάζουν σε όλα εκτός από τα σημεία στίξης που βρίσκονται σε αντίστοιχες θέσεις. Οι γραμμές από το παράδειγμά μου (μετά την ταξινόμηση) για τον αλγόριθμο σύγκρισης μοιάζουν με αυτό:

АбакановМихаилмаляр
ЁлкинаЭллакрановщица
ИвановаАлламаляр
ИвановАндрейслесарь

Λαμβάνοντας υπόψη ότι στον πίνακα με τις κλίμακες, τα κεφαλαία γράμματα στα ρωσικά έρχονται μετά τα πεζά γράμματα (στο τρίτο επίπεδο βαρύτερο από ), η ταξινόμηση φαίνεται απολύτως σωστή.

Όταν ορίζετε μια μεταβλητή LC_COLLATE=C φορτώνεται ένας ειδικός πίνακας που καθορίζει μια σύγκριση byte προς byte

static const uint32_t collseqwc[] =
{
  8, 1, 8, 0x0, 0xff,
  /* 1st-level table */
  6 * sizeof (uint32_t),
  /* 2nd-level table */
  7 * sizeof (uint32_t),
  /* 3rd-level table */
  L'x00', L'x01', L'x02', L'x03', L'x04', L'x05', L'x06', L'x07',
  L'x08', L'x09', L'x0a', L'x0b', L'x0c', L'x0d', L'x0e', L'x0f',

...
  L'xf8', L'xf9', L'xfa', L'xfb', L'xfc', L'xfd', L'xfe', L'xff'
};

Εφόσον στο Unicode το σημείο κωδικού Ё προηγείται του A, οι συμβολοσειρές ταξινομούνται ανάλογα.

Κείμενο και δυαδικοί πίνακες

Προφανώς, η σύγκριση συμβολοσειρών είναι μια εξαιρετικά κοινή λειτουργία και η ανάλυση πίνακα CTT αρκετά δαπανηρή διαδικασία. Για να βελτιστοποιηθεί η πρόσβαση στον πίνακα, μεταγλωττίζεται σε δυαδική μορφή με την εντολή localdef.

Ομάδα localdef δέχεται ως παραμέτρους ένα αρχείο με πίνακα εθνικών χαρακτηριστικών (επιλογή -i), όπου όλοι οι χαρακτήρες αντιπροσωπεύονται από κουκκίδες Unicode και ένα αρχείο αντιστοιχίας μεταξύ κουκκίδων Unicode και χαρακτήρων μιας συγκεκριμένης κωδικοποίησης (επιλογή -f). Ως αποτέλεσμα της εργασίας, δημιουργούνται δυαδικά αρχεία για την τοπική ρύθμιση με το όνομα που καθορίζεται στην τελευταία παράμετρο.

glibc υποστηρίζει δύο δυαδικές μορφές αρχείων: "παραδοσιακό" και "μοντέρνο".

Η παραδοσιακή μορφή σημαίνει ότι το όνομα της τοπικής ρύθμισης είναι το όνομα του υποκαταλόγου μέσα /usr/lib/locale/. Αυτός ο υποκατάλογος αποθηκεύει δυαδικά αρχεία LC_COLLATE, LC_CTYPE, LC_TIME και ούτω καθεξής. Αρχείο LC_IDENTIFICATION περιέχει το επίσημο όνομα της τοπικής ρύθμισης (που μπορεί να διαφέρει από το όνομα του καταλόγου) και σχόλια.

Η σύγχρονη μορφή περιλαμβάνει την αποθήκευση όλων των τοπικών ρυθμίσεων σε ένα ενιαίο αρχείο /usr/lib/locale/locale-archive, το οποίο αντιστοιχίζεται στην εικονική μνήμη όλων των διαδικασιών που χρησιμοποιούν glibc. Το όνομα τοπικής ρύθμισης στη σύγχρονη μορφή υπόκειται σε κάποια κανονικοποίηση - μόνο αριθμοί και γράμματα που έχουν μειωθεί σε πεζά γράμματα παραμένουν στα ονόματα κωδικοποίησης. Έτσι ru_RU.KOI8-R, θα αποθηκευτεί ως ru_RU.koi8r.

Τα αρχεία εισόδου αναζητούνται στον τρέχοντα κατάλογο, καθώς και σε καταλόγους /usr/share/i18n/locales/ и /usr/share/i18n/charmaps/ για αρχεία CTT και κωδικοποίηση αρχείων, αντίστοιχα.

Για παράδειγμα, η εντολή

localedef -i ru_RU -f MAC-CYRILLIC ru_RU.MAC-CYRILLIC

θα μεταγλωττίσει το αρχείο /usr/share/i18n/locales/ru_RU χρησιμοποιώντας αρχείο κωδικοποίησης /usr/share/i18n/charmaps/MAC-CYRILLIC.gz και αποθηκεύστε το αποτέλεσμα /usr/lib/locale/locale-archive κάτω από το όνομα ru_RU.μακκυριλλικό

Εάν ορίσετε τη μεταβλητή LANG = en_US.UTF-8 ο glibc θα αναζητήσει δυαδικά τοπικά αρχεία στην ακόλουθη σειρά αρχείων και καταλόγων:

/usr/lib/locale/locale-archive
/usr/lib/locale/en_US.UTF-8/
/usr/lib/locale/en_US/
/usr/lib/locale/enUTF-8/
/usr/lib/locale/en/

Εάν μια τοπική ρύθμιση εμφανίζεται τόσο σε παραδοσιακή όσο και σε μοντέρνα μορφή, τότε δίνεται προτεραιότητα στη σύγχρονη.

Μπορείτε να δείτε τη λίστα με τις μεταγλωττισμένες τοπικές ρυθμίσεις με την εντολή locale -a.

Προετοιμασία του συγκριτικού σας πίνακα

Τώρα, οπλισμένοι με τις γνώσεις, μπορείτε να δημιουργήσετε τον δικό σας ιδανικό πίνακα σύγκρισης χορδών. Αυτός ο πίνακας θα πρέπει να συγκρίνει σωστά τα ρωσικά γράμματα, συμπεριλαμβανομένου του γράμματος Ё, και ταυτόχρονα να λαμβάνει υπόψη τα σημεία στίξης σύμφωνα με τον πίνακα ASCII.

Η διαδικασία προετοιμασίας του δικού σας πίνακα ταξινόμησης αποτελείται από δύο στάδια: επεξεργασία του πίνακα βαρών και μεταγλώττιση του σε δυαδική μορφή με την εντολή localdef.

Για να προσαρμοστεί ο συγκριτικός πίνακας με ελάχιστο κόστος επεξεργασίας, στη μορφή ISO 14652 Παρέχονται ενότητες για τη ρύθμιση των βαρών ενός υπάρχοντος πίνακα. Η ενότητα ξεκινά με μια λέξη-κλειδί αναπαραγγελία-μετά και υποδεικνύοντας τη θέση μετά την οποία πραγματοποιείται η αντικατάσταση. Η ενότητα τελειώνει με τη γραμμή αναπαραγγελία-τέλος. Εάν είναι απαραίτητο να διορθωθούν πολλά τμήματα του πίνακα, τότε δημιουργείται μια ενότητα για κάθε τέτοια ενότητα.

Αντέγραψα νέες εκδόσεις των αρχείων iso14651_t1_common и ru_ru από το αποθετήριο glibc στον αρχικό μου κατάλογο ~/.local/share/i18n/locales/ και έκανα ελαφρά επεξεργασία της ενότητας LC_COLLATE в ru_ru. Οι νέες εκδόσεις αρχείων είναι πλήρως συμβατές με την έκδοσή μου glibc. Εάν θέλετε να χρησιμοποιήσετε παλαιότερες εκδόσεις αρχείων, θα πρέπει να αλλάξετε τα συμβολικά ονόματα και το μέρος όπου ξεκινά η αντικατάσταση στον πίνακα.

LC_COLLATE
% Copy the template from ISO/IEC 14651
copy "iso14651_t1"
reorder-after <U000D>
<U0020> <S0020>;<BASE>;<MIN>;<U0020> % SPACE
<U0021> <S0021>;<BASE>;<MIN>;<U0021> % EXCLAMATION MARK
<U0022> <S0022>;<BASE>;<MIN>;<U0022> % QUOTATION MARK
...
<U007D> <S007D>;<BASE>;<MIN>;<U007D> % RIGHT CURLY BRACKET
<U007E> <S007E>;<BASE>;<MIN>;<U007E> % TILDE
reorder-end
END LC_COLLATE

Στην πραγματικότητα, θα ήταν απαραίτητο να αλλάξετε τα πεδία LC_IDENTIFICATION ώστε να δείχνουν προς την τοποθεσία ru_MY, αλλά στο παράδειγμά μου αυτό δεν ήταν υποχρεωτικό, αφού απέκλεισα το αρχείο από την αναζήτηση τοπικών ρυθμίσεων τοπική-αρχείο.

Ότι localdef δούλεψε με αρχεία στον φάκελό μου μέσω μιας μεταβλητής Ι18ΝΠΑΘ Μπορείτε να προσθέσετε έναν επιπλέον κατάλογο για να αναζητήσετε αρχεία εισόδου και ο κατάλογος για την αποθήκευση δυαδικών αρχείων μπορεί να καθοριστεί ως διαδρομή με κάθετες:

$> I18NPATH=~/.local/share/i18n localedef -i ru_RU -f UTF-8 ~/.local/lib/locale/ru_MY.UTF-8

POSIX προτείνει ότι σε ΓΛΩΣΣΑ μπορείτε να γράψετε απόλυτες διαδρομές σε καταλόγους με αρχεία τοπικών ρυθμίσεων, ξεκινώντας με κάθετο προς τα εμπρός, αλλά glibc в Linux Όλες οι διαδρομές μετρώνται από τον βασικό κατάλογο, ο οποίος μπορεί να παρακαμφθεί μέσω μιας μεταβλητής LOCPATH. Μετά την εγκατάσταση LOCPATH=~/.local/lib/locale/ όλα τα αρχεία που σχετίζονται με την τοπική προσαρμογή θα αναζητηθούν μόνο στον φάκελό μου. Αρχείο τοπικών ρυθμίσεων με το σύνολο μεταβλητών LOCPATH αγνόησε.

Εδώ είναι το καθοριστικό τεστ:

$> LANG=ru_MY.UTF-8 LOCPATH=~/.local/lib/locale/ sort buhg.txt
Абаканов Михаил;маляр
Ёлкина Элла;крановщица
Иванов Андрей;слесарь
Иванова Алла;адвокат

Ζήτω! Τα καταφέραμε!

Ορισμένα σφάλματα

Έχω ήδη απαντήσει στις ερωτήσεις σχετικά με την ταξινόμηση χορδών που τέθηκαν στην αρχή, αλλά υπάρχουν ακόμα μερικές ερωτήσεις σχετικά με σφάλματα - ορατά και αόρατα.

Ας επιστρέψουμε στο αρχικό πρόβλημα.

Και το πρόγραμμα είδος και το πρόγραμμα ενταχθούν χρησιμοποιήστε τις ίδιες συναρτήσεις σύγκρισης συμβολοσειρών από glibc. Πώς έγινε αυτό ενταχθούν έδωσε ένα σφάλμα ταξινόμησης σε σειρές ταξινομημένες από την εντολή είδος σε τοπικό επίπεδο en_US.UTF-8? Η απάντηση είναι απλή: είδος συγκρίνει ολόκληρη τη συμβολοσειρά και ενταχθούν συγκρίνει μόνο το κλειδί, το οποίο από προεπιλογή είναι η αρχή της συμβολοσειράς μέχρι τον πρώτο χαρακτήρα κενού. Στο παράδειγμά μου, αυτό οδήγησε σε ένα μήνυμα σφάλματος επειδή η ταξινόμηση των πρώτων λέξεων στις γραμμές δεν ταιριάζει με την ταξινόμηση των πλήρων γραμμών.

Μικρός λοβός "C" εγγυάται ότι στις ταξινομημένες συμβολοσειρές θα ταξινομηθούν και οι αρχικές υποσυμβολοσειρές μέχρι το πρώτο διάστημα, αλλά αυτό μόνο καλύπτει το σφάλμα. Είναι δυνατό να επιλέξετε δεδομένα (άτομα με τα ίδια επώνυμα, αλλά διαφορετικά ονόματα) που, χωρίς το μήνυμα σφάλματος, θα έδιναν ένα εσφαλμένο αποτέλεσμα συγχώνευσης αρχείων. Αν θέλουμε ενταχθούν συγχωνεύτηκαν γραμμές αρχείων με το πλήρες όνομα, τότε ο σωστός τρόπος θα ήταν να προσδιορίσετε ρητά το διαχωριστικό πεδίου και να ταξινομήσετε κατά το πεδίο κλειδιού και όχι κατά ολόκληρη τη γραμμή. Σε αυτήν την περίπτωση, η συγχώνευση θα προχωρήσει σωστά και δεν θα υπάρχουν σφάλματα σε καμία τοπική ρύθμιση:

$> sort -t ; -k 1 buhg.txt > buhg.srt
$> sort -t ; -k 1 mail.txt > mail.srt
$> join -t ; buhg.srt mail.srt > result

Παράδειγμα κωδικοποίησης που εκτελέστηκε με επιτυχία CP1251 περιέχει ένα άλλο σφάλμα. Γεγονός είναι ότι σε όλες τις γνωστές μου διανομές Linux λείπουν τα πακέτα μεταγλωττισμένες τοπικές ρυθμίσεις ru_RU.CP1251. Εάν δεν βρεθεί η μεταγλωττισμένη τοποθεσία, τότε είδος χρησιμοποιεί σιωπηλά μια σύγκριση byte-byte, κάτι που παρατηρήσαμε.

Παρεμπιπτόντως, υπάρχει ένα άλλο μικρό σφάλμα που σχετίζεται με τη μη προσβασιμότητα των μεταγλωττισμένων τοπικών ρυθμίσεων. Ομάδα LOCPATH=/tmp locale -a θα δώσει μια λίστα με όλες τις τοπικές ρυθμίσεις τοπική-αρχείο, αλλά με το σύνολο μεταβλητών LOCPATH για όλα τα προγράμματα (συμπεριλαμβανομένων των περισσότερων τοπικός) αυτές οι τοπικές ρυθμίσεις δεν θα είναι διαθέσιμες.

$> LOCPATH=/tmp locale -a | grep en_US
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_COLLATE to default locale: No such file or directory
en_US
en_US.iso88591
en_US.iso885915
en_US.utf8

$> LC_COLLATE=en_US.UTF-8 sort --debug
sort: using ‘en_US.UTF-8’ sorting rules

$> LOCPATH=/tmp LC_COLLATE=en_US.UTF-8 sort --debug
sort: using simple byte comparison

Συμπέρασμα

Εάν είστε προγραμματιστής που συνηθίζει να πιστεύει ότι οι συμβολοσειρές είναι ένα σύνολο byte, τότε η επιλογή σας LC_COLLATE=C.

Εάν είστε γλωσσολόγος ή μεταγλωττιστής λεξικών, τότε καλύτερα να κάνετε μεταγλώττιση στην τοπική σας γλώσσα.

Εάν είστε απλός χρήστης, τότε απλά πρέπει να συνηθίσετε στο γεγονός ότι η εντολή ls -a εξάγει αρχεία που ξεκινούν με μια τελεία αναμεμειγμένη με αρχεία που ξεκινούν με ένα γράμμα και Διοικητής των μεσάνυχτων, το οποίο χρησιμοποιεί τις εσωτερικές του λειτουργίες για την ταξινόμηση ονομάτων, βάζει αρχεία που ξεκινούν με μια τελεία στην αρχή της λίστας.

παραπομπές

Αναφορά Νο. 10 Αλγόριθμος ταξινόμησης Unicode

Βάρη χαρακτήρων στο unicode.org

ΜΕΘ — υλοποίηση βιβλιοθήκης για εργασία με Unicode από την IBM.

Δοκιμή ταξινόμησης με χρήση ΜΕΘ

Βάρη χαρακτήρων ISO 14651

Περιγραφή της μορφής αρχείου με κλίμακες ISO 14652

Συζήτηση σύγκρισης χορδών σε glibc

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο