2016-07-20 13 views
3

So wissen wir alle, dass das Dateisystem auf Mac OS diese verrückte Eigenschaft hat, vollständig zerlegt UTF-8 zu verwenden. Wenn Sie beispielsweise POSIX-APIs wie realpath() aufrufen, erhalten Sie eine solche vollständig zerlegte UTF-8-Zeichenfolge von Mac OS zurück. Bei Verwendung von APIs wie scheint es jedoch auch möglich zu sein, vorkompostiertes UTF-8 zu übergeben.Warum erlaubt die C-Laufzeit unter Mac OS sowohl vorkomposiertes als auch zerlegtes UTF-8?

Hier ist ein kleines Demo-Programm, das versucht, eine Datei mit dem Namen ä zu öffnen. Der erste Aufruf an übergibt eine vorkompilierte UTF-8-Zeichenfolge, der zweite Aufruf übergibt eine zerlegte UTF-8-Zeichenfolge und zu meiner Überraschung funktionieren beide. Ich würde erwarten, dass nur der zweite funktioniert, aber auch vorkomposiertes UTF-8 funktioniert.

#include <stdio.h> 

int main(int argc, char *argv[]) 
{ 
    FILE *fp, *fp2; 

    fp = fopen("\xc3\xa4", "rb");  // ä as precomposed UTF-8 
    fp2 = fopen("\x61\xcc\x88", "rb"); // ä as decomposed UTF-8 

    printf("CHECK: %p %p\n", fp, fp2); 

    if(fp) fclose(fp); 
    if(fp2) fclose(fp2); 

    return 0; 
} 

Nun zu meinen Fragen:

  1. Ist das definierte Verhalten? d. h. ist es erlaubt, vorkompostierte UTF-8 an POSIX-APIs weiterzugeben oder sollte ich immer zerlegtes UTF-8 passieren?

  2. Wie können Funktionen wie sogar wissen, ob die übergebene Datei vorkomposiertes oder zerlegtes UTF-8 enthält? Könnte dies nicht sogar zu allen möglichen Problemen führen, z.B. falsche Dateien geöffnet werden, weil die übergebene Zeichenfolge auf zwei verschiedene Arten interpretiert werden kann und somit möglicherweise auf zwei verschiedene Dateien verweist? Das verwirrt mich etwas.

EDIT

die Verwirrung komplett zu machen, dieses seltsame Verhalten scheint nicht einmal E/A-Datei beschränkt. Werfen Sie einen Blick auf diesen Code:

#include <stdio.h> 

int main(int argc, char *argv[]) 
{ 
    printf("\xc3\xa4\n"); 
    printf("\x61\xcc\x88\n"); 

    return 0; 
} 

Beide printf Anrufe tun genau das gleiche, das heißt, sie beide drucken den Charakter ä, der erste Anruf mit vorverfasstes UTF-8 und die zweite mit zerlegten UTF-8. Es ist wirklich komisch.

+2

Ich nehme an, * dass der Pfad immer auf der Dateisystemebene HFS + zerlegt ist, also ist es egal, was Sie übergeben. –

+1

Das Dateisystem ist auch case insensitive, nicht wahr? Dies wäre dasselbe wie die falschen Buchstaben an "fopen" zu übergeben. Der interne Mechanismus zur Suche nach einem Dateinamen behandelt wahrscheinlich beide Fälle auf die gleiche Weise. –

+0

Aber was kam 'readdir' zurück? –

Antwort

1

Es sind zwei verschiedene Arten von Gleichwertigkeit in Unicode-Strings: Eines ist kanonische Äquivalenz, und ein anderer ist Kompatibilität. Da Ihre Frage über Zeichenfolgen ist, die von der Software als identisch betrachtet werden, konzentrieren wir uns auf kanonische Äquivalenz (OTOH, Kompatibilität ermöglicht semantische Unterschiede, so ist es off-topic in dieser Frage).

aus Unicode equivalence in Wikipedia Unter Hinweis:

-Code Punktfolgen, die alskanonisch äquivalent definiert sind das gleiche Aussehen und die Bedeutung, wenn es gedruckt haben angenommen oder angezeigt. Zum Beispiel ist der Codepunkt U + 006E (der lateinische Kleinbuchstabe "n") gefolgt von U + 0303 (der Kombinationsschild "◌") definiert durch Unicode, um kanonisch äquivalent zu dem einzelnen Codepunkt U + 00F1 zu sein (der Kleinbuchstabe "ñ" des spanischen Alphabets).Daher sollten diese Sequenzen in der gleichen Weise angezeigt werden, sollte in auf die gleiche Weise von Anwendungen wie Namen alphabetisch oder Suche behandelt werden, und können für einander ersetzt werden.

Mit anderen Worten, wenn zwei Saiten sind kanonisch äquivalent, sollte die Software die beiden Strings betrachten genau die gleiche Sache darstellen. Also, MacOS macht hier die richtige Sache: Sie haben zwei verschiedene UTF-8 Strings (eine zerlegt, eine andere vorkomposiert), aber sie sind kanonisch äquivalent, so dass sie auf das gleiche Objekt abgebildet werden (der gleiche Dateiname in Ihrem Beispiel) . Das ist richtig (denken Sie daran, die "sollte in der gleichen Weise durch Anwendungen wie alphabetisch Namen oder Suche behandelt werden, und kann für einander ersetzt werden" Zeile im Zitat oben).

Ich verstehe nicht wirklich Ihr zweites Beispiel über printf(). Ja, sowohl ein zerlegtes Zeichen als auch ein vorkompariertes Zeichen geben die gleiche Ausgabe wieder. Genau das ist der Punkt in der doppelten Darstellung von Zeichen, die von Unicode unterstützt werden: Sie können wählen, ob Sie ein kombiniertes Zeichen mit einer vorkompilierten Bytefolge oder eine zerlegte Bytefolge darstellen. Sie drucken dasselbe visuelle Ergebnis, aber ihre Darstellung ist anders. Wenn beide Darstellungen kanonisch entsprechen (in einigen Fällen sind sie, in einigen Fällen sind sie nicht), dann muss das System sie als zwei Darstellungen des gleichen Objekts betrachten.

Um dies alles bequemer in Ihrer Software zu verwalten, sollten Sie vor der Arbeit mit ihnen normalize your Unicode strings.