2009-04-19 10 views
10

J'utilise le code suivant pour enregistrer les caractères chinois dans un fichier .txt, mais lorsque je l'ai ouvert avec Wordpad, je ne pouvais pas le lire.Comment enregistrer les caractères chinois dans un fichier avec Java?

StringBuffer Shanghai_StrBuf = new StringBuffer("\u4E0A\u6D77"); 
boolean Append = true; 

FileOutputStream fos; 
fos = new FileOutputStream(FileName, Append); 
for (int i = 0;i < Shanghai_StrBuf.length(); i++) { 
    fos.write(Shanghai_StrBuf.charAt(i)); 
} 
fos.close(); 

Que puis-je faire? Je sais que si je coupe et colle des caractères chinois dans Wordpad, je peux l'enregistrer dans un fichier .txt. Comment je fais ça en Java?

+0

duplication possible de [Qu'est-ce que le codage de caractères et pourquoi devrais-je m'en préoccuper?] (http://stackoverflow.com/questions/10611455/what-is-character-encoding-and -why-should-i-bother-with-it) – Raedwald

Répondre

10

Il y a plusieurs facteurs à l'œuvre:

  • Les fichiers texte n'ont pas de métadonnées intrinsèques pour décrire leur encodage (pour toutes les discussions sur les taxes en tranches d'angle, il y a des raisons XML est populaire)
  • L'encodage par défaut pour Windows est toujours un 8bit (ou double octet) caractère « ANSI » set avec une gamme limitée de valeurs - fichiers texte écrit dans ce format ne sont pas portables
  • Pour dire un fichier Unicode à partir d'un Fichier ANSI, les applications Windows s'appuient sur la présence d'un byte order mark au début du fichier (not strictly true - Raymond Chen explains). En théorie, la nomenclature est là pour vous indiquer le endianess (ordre des octets) des données. Pour UTF-8, même s'il n'y a qu'un seul ordre d'octets, les applications Windows s'appuient sur les octets de marqueur pour déterminer automatiquement qu'il s'agit d'Unicode (bien que vous remarquiez que le Bloc-notes dispose d'une option d'encodage).
  • Il est faux de dire que Java est cassé parce qu'il n'écrit pas automatiquement une nomenclature UTF-8. Sur les systèmes Unix, ce serait une erreur d'écrire une nomenclature dans un fichier script, par exemple, et de nombreux systèmes Unix utilisent UTF-8 comme encodage par défaut. Il y a des moments où vous ne voulez pas sous Windows, que ce soit, comme lorsque vous l'ajout de données à un fichier existant: fos = new FileOutputStream(FileName,Append);

Voici une méthode de annexant fiable des données UTF-8 dans un fichier:

private static void writeUtf8ToFile(File file, boolean append, String data) 
     throws IOException { 
    boolean skipBOM = append && file.isFile() && (file.length() > 0); 
    Closer res = new Closer(); 
    try { 
     OutputStream out = res.using(new FileOutputStream(file, append)); 
     Writer writer = res.using(new OutputStreamWriter(out, Charset 
      .forName("UTF-8"))); 
     if (!skipBOM) { 
     writer.write('\uFEFF'); 
     } 
     writer.write(data); 
    } finally { 
     res.close(); 
    } 
    } 

Utilisation:

public static void main(String[] args) throws IOException { 
    String chinese = "\u4E0A\u6D77"; 
    boolean append = true; 
    writeUtf8ToFile(new File("chinese.txt"), append, chinese); 
    } 

note: si le fichier existait déjà et que vous avez choisi d'ajouter les données existantes et n'a pas été UTF-8 codé, la seule chose que le code w mal créer est un gâchis.

Voici le type Closer utilisé dans ce code:

public class Closer implements Closeable { 
    private Closeable closeable; 

    public <T extends Closeable> T using(T t) { 
    closeable = t; 
    return t; 
    } 

    @Override public void close() throws IOException { 
    if (closeable != null) { 
     closeable.close(); 
    } 
    } 
} 

Ce code fait une meilleure estimation de style de Windows sur la façon de lire le fichier en fonction des marques d'ordre d'octet:

private static final Charset[] UTF_ENCODINGS = { Charset.forName("UTF-8"), 
     Charset.forName("UTF-16LE"), Charset.forName("UTF-16BE") }; 

    private static Charset getEncoding(InputStream in) throws IOException { 
    charsetLoop: for (Charset encodings : UTF_ENCODINGS) { 
     byte[] bom = "\uFEFF".getBytes(encodings); 
     in.mark(bom.length); 
     for (byte b : bom) { 
     if ((0xFF & b) != in.read()) { 
      in.reset(); 
      continue charsetLoop; 
     } 
     } 
     return encodings; 
    } 
    return Charset.defaultCharset(); 
    } 

    private static String readText(File file) throws IOException { 
    Closer res = new Closer(); 
    try { 
     InputStream in = res.using(new FileInputStream(file)); 
     InputStream bin = res.using(new BufferedInputStream(in)); 
     Reader reader = res.using(new InputStreamReader(bin, getEncoding(bin))); 
     StringBuilder out = new StringBuilder(); 
     for (int ch = reader.read(); ch != -1; ch = reader.read()) 
     out.append((char) ch); 
     return out.toString(); 
    } finally { 
     res.close(); 
    } 
    } 

Utilisation :

public static void main(String[] args) throws IOException { 
    System.out.println(readText(new File("chinese.txt"))); 
    } 

(System.out utilise l'encodage par défaut, donc si elle imprime quoi que ce soit Sensi ble dépend de votre plate-forme et configuration.)

+0

Très bien! De toutes les réponses que j'ai essayées, la vôtre est la meilleure! Mais comment lire unicode sur le fichier que je viens de sauvegarder? J'ai utilisé mon utilitaire pour le lire et j'ai obtenu ceci: 00ef 00bb 00bf 00e4 00b8 008a 00e6 00b5 00b7 Je peux voir du bloc-notes qu'il y a deux caractères chinois: Shang & Hai, mais comment Java peut-il les lire? Merci ! – Frank

+0

J'ai ajouté du code qui fait une meilleure estimation de la lecture de fichiers texte arbitraires. – McDowell

+0

Super! C'est exactement ce que je cherche! Je souhaite que cela fasse partie du paquet Java de Sun, pas quelque chose dont nous devons nous inquiéter. Merci ! – Frank

4

Si vous pouvez compter que le codage de caractères par défaut est UTF-8 (ou un autre codage Unicode), vous pouvez utiliser les éléments suivants:

Writer w = new FileWriter("test.txt"); 
    w.append("上海"); 
    w.close(); 

Le moyen le plus sûr est de toujours spécifier explicitement le codage:

Writer w = new OutputStreamWriter(new FileOutputStream("test.txt"), "UTF-8"); 
    w.append("上海"); 
    w.close(); 

P.S. Vous pouvez utiliser n'importe quel caractère Unicode dans le code source Java, même en tant que nom de méthode et de variable, si le paramètre -encoding de javac est configuré correctement. Cela rend le code source plus lisible que le formulaire \uXXXX échappé.

+0

Je voudrais, mais depuis que j'utilise NetBeans, après avoir coupé et collé le chinois dans le fichier java et l'ai enregistré, il n'apparaîtra pas (seulement voir ?? ?) lorsque je rouvre le fichier java dans Netbeans. – Frank

+0

Peut-être que NetBeans est configuré pour utiliser un codage non-Unicode ou que la police de l'éditeur n'a pas tous les caractères Unicode.Je n'utilise pas NetBeans, mais à partir de son fichier d'aide, je vois que vous définissez l'encodage sur Propriétés du projet | Sources | Codage. –

+0

Etes-vous sûr de savoir quel encodage du fichier a été enregistré, si vous l'avez enregistré avec un autre éditeur? –

1

Voici une façon parmi d'autres. En gros, nous sommes en train de préciser que la conversion se fait en UTF-8 avant la sortie octets au FileOutputStream:

String FileName = "output.txt"; 

StringBuffer Shanghai_StrBuf=new StringBuffer("\u4E0A\u6D77"); 
boolean Append=true; 

Writer writer = new OutputStreamWriter(new FileOutputStream(FileName,Append), "UTF-8"); 
writer.write(Shanghai_StrBuf.toString(), 0, Shanghai_StrBuf.length()); 
writer.close(); 

J'ai vérifié manuellement ce contre les images à http://www.fileformat.info/info/unicode/char/. À l'avenir, veuillez suivre les normes de codage Java, y compris les noms de variables minuscules. Il améliore la lisibilité.

1

Essayez ceci,

StringBuffer Shanghai_StrBuf=new StringBuffer("\u4E0A\u6D77"); 
    boolean Append=true; 

    Writer out = new BufferedWriter(new OutputStreamWriter(
     new FileOutputStream(FileName,Append), "UTF8")); 
    for (int i=0;i<Shanghai_StrBuf.length();i++) out.write(Shanghai_StrBuf.charAt(i)); 
    out.close(); 
3

Soyez très prudent avec les approches proposées. Même en spécifiant le codage pour le fichier comme suit:

Writer w = nouveau OutputStreamWriter (nouveau FileOutputStream ("test.txt"), "UTF-8");

ne fonctionnera pas si vous utilisez un système d'exploitation comme Windows. Même la définition de la propriété système pour file.encoding sur UTF-8 ne résout pas le problème. C'est parce que Java ne parvient pas à écrire une marque d'ordre des octets (BOM) pour le fichier. Même si vous spécifiez l'encodage lors de l'écriture dans un fichier, l'ouverture du même fichier dans une application telle que Wordpad affichera le texte en tant que fouillis car il ne détecte pas la nomenclature. J'ai essayé d'exécuter les exemples ici dans Windows (avec un encodage plateforme/conteneur de CP1252).

Le bug suivant existe pour décrire le problème en Java:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058

La solution pour l'instant est de marquer écrire l'ordre des octets vous-même pour que le fichier ouvre correctement dans d'autres applications.Voir ce pour plus de détails sur la nomenclature:

http://mindprod.com/jgloss/bom.html

et pour une solution plus adéquate, consultez le lien suivant:

http://tripoverit.blogspot.com/2007/04/javas-utf-8-and-unicode-writing-is.html

+0

Je m'attendais à avoir une crevette, maintenant j'ai trouvé un requin et un tueur de requins! Merci. Dans la "bonne solution" que vous avez posté, pourquoi sont les "init();" lignes commentées dans Close() et read()? Dois-je les décommenter pour fonctionner correctement? – Frank

+0

Pas tout à fait sûr, mais cela ne devrait pas avoir d'importance pour l'écriture, seulement pour la lecture. Si vous lisez un fichier UTF-8, vous devez ignorer la nomenclature, car cela désoriente Java - c'est ce que fait la méthode init. Peut-être vaut-il la peine de contacter l'auteur du blog pour connaître les raisons qui le justifient. Désolé, je ne peux pas être plus utile. – Jon

+0

Vous pouvez éventuellement supprimer la partie de lecture de code. On dirait qu'Apache a essayé de créer son propre BOMExclusionReader, voir: https://issues.apache.org/jira/browse/IO-178 – Jon