2009-10-09 6 views
12

Ich habe es mit Code zu tun, der verschiedene IO-Operationen mit Dateien durchführt, und ich möchte, dass er mit internationalen Dateinamen umgehen kann. Ich arbeite an einem Mac mit Java 1.5, und wenn ein Dateiname Unicode-Zeichen enthält, die Surrogate erfordern, scheint die JVM die Datei nicht zu finden. Zum Beispiel meine Testdatei ist:Java kann eine Datei mit Ersatz-Unicode-Werten im Dateinamen nicht öffnen?

"草鷗外.gif", die in die Java Zeichen \u8349\uD85B\uDFF6\u9DD7\u5916.gif

kaputt geht, wenn ich eine Datei aus diesem Dateinamen erstellen, kann ich es nicht öffnen, weil ich eine FileNotFound Exception. mit diesem auch auf den Ordner, die Datei enthält, wird fehlschlagen:

File[] files = folder.listFiles(); 
for (File file : files) { 
    if (!file.exists()) { 
     System.out.println("Failed to find File"); //Fails on the surrogate filename 
    } 
} 

Die meisten der Code, den ich mit eigentlich zu tun bin von der Form sind:

FileInputStream instream = new FileInputStream(new File("草鷗外.gif")); 
// operations follow 

Gibt es irgendeine Weise, die ich dieses Problem lösen können, entwischen Sie die Dateinamen oder öffnen Sie die Dateien anders?

+0

Was ist der Wert von Charset.defaultCharset() in Ihrer Umgebung? –

+2

(Leider hat StackOverflow auch ein Problem mit Surrogate und hat den U + 26FF6-Ideographen aus der Frage entfernt) – bobince

+0

Können Sie angeben, was System.getProperty ("file.encoding") zurückgibt? Versuchen Sie, die Codierung java-dfile.encoding = ENCODING_GOES_HERE zu ändern, wenn das Systemgebietsschema nicht geändert wird. Wenn dies auch nicht funktioniert, warten wir auf einen Experten, um es zu lösen. – JCasso

Antwort

4

Wenn das Standardgebietsschema Ihrer Umgebung diese Zeichen nicht enthält, können Sie die Datei nicht öffnen.

See: File.exists() fails with unicode characters in name

Edit: Okay .. Was Sie brauchen, ist das Systemgebietsschema zu ändern. Welches Betriebssystem Sie auch verwenden.

bearbeiten:

See: How can I open files containing accents in Java?

See: JFileChooser on Mac cannot see files named by Chinese chars?

+0

Ist dies nicht möglich, ohne das Systemgebietsschema zu ändern? Das Programm, das ich erstelle, muss an jedem Ort ausgeführt werden, und ich sollte in der Lage sein, diese Zeichen einzugeben und mit diesen Dateien sogar in einem US/Englisch Gebietsschema umzugehen. – Bear

+0

Schlechte Lösung - weil App auf Usern läuft, die nicht auf meinem Computer sitzen. Und haben ein anderes Gebietsschema, und sie haben keinen richtigen Administrator, um dies zu tun. –

+0

AFAIK gibt es keine andere Lösung. Diese Einschränkung kommt mit Sun/Oracle Java. Sie können JFileChooser ausprobieren, wenn das Anzeigen eines Sicherungsdialogs für Ihre Benutzer für Sie in Ordnung ist. – JCasso

7

Ich vermute, eine von Java oder Mac ist CESU-8 statt korrekte UTF-8 verwenden. Java verwendet "modifizierte UTF-8" (eine leichte Variante von CESU-8) für eine Vielzahl von internen Zwecken, aber ich wusste nicht, dass es es als Dateisystem/defaultCharset verwenden könnte. Leider habe ich weder Mac noch Java hier zum testen.

"Modifiziert" ist eine modifizierte Art zu sagen "schlecht abgehört". Anstelle der Verwendung eine Vier-Byte-UTF-8-Sequenz für die zusätzlichen (nicht-BMP) Zeichen, wie die Ausgabe & # x26FF6 ;:

\xF0\xA6\xBF\xB6 

gibt sie eine UTF-8-codierte Sequenz, die für jeden des Surrogate:

\xED\xA1\x9B\xED\xBF\xB6 

Dies ist keine gültige UTF-8-Sequenz, aber viele Decoder erlauben es trotzdem. Das Problem ist, wenn Sie das durch einen echten UTF-8-Encoder hin- und herdrehen, haben Sie eine andere Zeichenfolge, die Vier-Byte-Eins oben. Versuchen Sie, auf die Datei mit diesem Namen und Boom zuzugreifen! Scheitern.

So lassen Sie uns zuerst nur überprüfen, wie Dateinamen unter dem aktuellen Dateisystem tatsächlich gespeichert sind, eine Plattform, die Bytes für Dateinamen wie Python 2.x verwendet:

$ python 
Python 2.x.something (blah blah) 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import os 
>>> os.listdir('.') 

Auf meinem Dateisystem (Linux, ext4, UTF -8), der Dateiname "草 & # x26FF6; 鷗 外.gif“kommt als:

['\xe8\x8d\x89\xf0\xa6\xbf\xb6\xe9\xb7\x97\xe5\xa4\x96.gif'] 

das ist, was Sie wollen. Wenn du das bekommst, ist es wahrscheinlich, dass Java es falsch macht. Wenn Sie die länger Sechs-Byte-Zeichen-Version erhalten:

['\xe8\x8d\x89\xed\xa1\x9b\xed\xbf\xb6\xe9\xb7\x97\xe5\xa4\x96.gif'] 

es ist wahrscheinlich OS X es falsch zu machen ... speichert es immer Dateinamen wie das? (Oder haben die Dateien kommen von woanders ursprünglich?) Was ist, wenn Sie die Datei auf die ‚richtige‘ Version umbenennen ?:

os.rename('\xe8\x8d\x89\xed\xa1\x9b\xed\xbf\xb6\xe9\xb7\x97\xe5\xa4\x96.gif', '\xe8\x8d\x89\xf0\xa6\xbf\xb6\xe9\xb7\x97\xe5\xa4\x96.gif') 
+2

Nicht wirklich ein Fehler, da es Teil der Spezifikation ist (auch wenn es oft verwirrend ist.) – finnw

+0

Das Ergebnis der Python-Befehle war der richtige Dateiname, den Sie zuerst aufgelistet haben, also muss Java nicht nett sein. – Bear

+0

Oh, das ist bedauerlich. Selbst wenn Sie die kaputte CESU-8-Situation erkannt haben, kann ich mir keine Möglichkeit vorstellen, sie zu umgehen und eine byte-orientierte Dateinamenschnittstelle zu erhalten. :-(Sie müssen die Surrogate möglicherweise explizit verbieten, bis Sun sie repariert. Wie schlecht. – bobince

3

Dies erwies sich als ein Problem mit dem Mac JVM sein (getestet auf 1.5 und 1.6). Dateinamen, die ergänzende Zeichen/Ersatzpaare enthalten, können nicht mit der Java-Dateiklasse aufgerufen werden. Am Ende schrieb ich eine JNI-Bibliothek mit Carbon-Calls für die Mac-Version des Projekts (ick). Ich vermute, dass das CESU-8-Problem bobince erwähnt wurde, da der JNI-Aufruf, um UTF-8-Zeichen zu erhalten, eine CESU-8-Zeichenfolge zurückgegeben hat. Sieht nicht so aus, als könntest du wirklich herumkommen.

0

Es ist ein Fehler in der alten skool Java File API, vielleicht nur auf einem Mac? Wie auch immer, das neue java.nio api funktioniert viel besser. Ich habe mehrere Dateien mit Unicode-Zeichen und Inhalten, die mit java.io.File und verwandten Klassen nicht geladen werden konnten. Nachdem ich meinen gesamten Code konvertiert habe, um java.nio.Path zu verwenden, hat ALLES angefangen zu arbeiten. Und ich ersetzt org.apache.commons.io.FileUtils (die das gleiche Problem hat) mit java.nio.Files ...

... und sicher sein, den Inhalt der Datei mit einem geeigneten Zeichensatz zu lesen und zu schreiben, zum Beispiel: Files.readAllLines (myPath, StandardCharsets.UTF_8)