【Java基礎特集】符号化と文字化けし(04)---出力時の符号化と文字化けし
14499 ワード
package example.encoding;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedMap;
/** *//**
* <pre>
* The Class IOEncodeTest is a tester class for java encoding. Mainnaly contains
* two parts:
* 1.Test written by FileWriter, with or without given character encoding value
* 2.Test written by OutputStreamWriter, with or without given character encoding value
* </pre>
*
* @author Paul Lin
* @version 1.0
*/
public class OutputEncodingTest {
private static String word = "Hello world! ";
private static final String ENCODING_EN = "ISO-8859-1";
private static final String ENCODING_CN = "GB2312";
private static final String ENCODING_UTF = "UTF-8";
private static final String DEFAULT_SYSTEM_ENCODING = System
.getProperty("file.encoding");
/** *//**
* The main method.
*
* @param args the arguments
*/
public static void main(String args[]) {
OutputEncodingTest tester = new OutputEncodingTest();
tester.testFileWriter();
tester.testOutputStreamWriter();
}
/** *//**
* Test file writer.
*/
public void testFileWriter() {
// Create test result folder
String resultFolder = createResultFolder(System
.getProperty("user.language"), getBasePath());
// With default platform encoding
writeByFileWriter(word, resultFolder);
// With given system file.encoding property
writeByFileWriter(word, ENCODING_EN, resultFolder);
writeByFileWriter(word, ENCODING_CN, resultFolder);
writeByFileWriter(word, ENCODING_UTF, resultFolder);
}
/** *//**
* Test output stream writer.
*/
public void testOutputStreamWriter() {
// Create test result folder
String resultFolder = createResultFolder(System
.getProperty("user.language"), getBasePath());
// With default platform encoding
writeByOutputStreamWriter(word, resultFolder);
// With given system file.encoding property
writeByOutputStreamWriter(word, ENCODING_EN, resultFolder);
writeByOutputStreamWriter(word, ENCODING_CN, resultFolder);
writeByOutputStreamWriter(word, ENCODING_UTF, resultFolder);
}
/** *//**
* Prints the available charset.
*/
public void printAvailableCharset() {
SortedMap<String, Charset> charsets = Charset.availableCharsets();
Set<String> charsetKeys = charsets.keySet();
System.out.println("
<<<< Canonical name -- Display name -- "
+ " Can encode >>>>
");
Iterator<String> i = charsetKeys.iterator();
while (i.hasNext()) {
String key = (String) i.next();
Charset charset = (Charset) charsets.get(key);
String displayName = charset.displayName();
boolean canEncode = charset.canEncode();
System.out.println(key + " - " + displayName + " - " + canEncode);
}
}
/** *//**
* Write by file writer.
*
* @param content the content
*/
private void writeByFileWriter(String content, String destination) {
String defaultEncoding = System.getProperty("file.encoding");
System.out.println("Using default system encoding: " + defaultEncoding);
writeByFileWriter(content, defaultEncoding, destination);
}
/** *//**
* Write by file writer.
*
* @param content the content
* @param encoding the encoding
*/
private void writeByFileWriter(String content, String encoding,
String destination) {
printDebugInformation("FileWriter", encoding, content);
// Get system default encoding
String defaultEncoding = System.getProperty("file.encoding");
// Reset underlying platform character encoding
if (!defaultEncoding.equalsIgnoreCase(encoding)) {
System.setProperty("file.encoding", encoding);
}
// Save as file with given encoding value
String file = returnFileName(destination, "write_by_filewriter_",
encoding, ".txt");
try {
Writer writer = new BufferedWriter(new FileWriter(file));
writer.write(content);
writer.flush();
writer.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
// Reset character encoding to system default value
resetDefaultSystemEncoding();
}
/** *//**
* Write by output stream writer.
*
* @param content the content
*/
private void writeByOutputStreamWriter(String content, String destination) {
String defaultEncoding = System.getProperty("file.encoding");
System.out.println("Using default system encoding: " + defaultEncoding);
writeByOutputStreamWriter(content, defaultEncoding, destination);
}
/** *//**
* Write by output stream writer.
*
* @param content the content
* @param encoding the encoding
*/
private void writeByOutputStreamWriter(String content, String encoding,
String destination) {
printDebugInformation("OutputStreamWriter", encoding, content);
// Save as file with given encoding value
String file = returnFileName(destination,
"write_by_outputStreamWriter_", encoding, ".txt");
try {
Writer writer = new PrintWriter(
new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(file), encoding)));
writer.write(content);
writer.flush();
writer.close();
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
} catch (UnsupportedEncodingException uee) {
uee.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
// Reset character encoding to system default value
resetDefaultSystemEncoding();
}
/** *//**
* Gets the base path.
*
* @return the base path
*/
private String getBasePath() {
StringBuffer finalPath = new StringBuffer();
String dir = System.getProperty("user.dir");
finalPath.append(dir);
finalPath.append((dir.endsWith("\\") || dir.endsWith("/")) ? "" : "/");
finalPath.append("src").append("/");
finalPath.append("example").append("/");
finalPath.append("encoding").append("/");
return finalPath.toString();
}
/** *//**
* Return file name.
*
* @param basePath the base path
* @param prefix the prefix
* @param content the content
* @param subfix the subfix
*
* @return the string
*/
private String returnFileName(String basePath, String prefix,
String content, String subfix) {
StringBuffer name = new StringBuffer(basePath);
if ((!basePath.endsWith("\\") && (!basePath.endsWith("/")))) {
name.append("/");
}
name.append(prefix);
name.append(content);
name.append(subfix);
return name.toString();
}
/** *//**
* Creates the result folder.
*
* @param platform the platform
* @param fullPath the full path
*
* @return the string
*/
private String createResultFolder(String platform, String fullPath) {
StringBuffer resultFolder = new StringBuffer();
if (fullPath.endsWith("\\") || fullPath.endsWith("/")) {
resultFolder.append(fullPath);
} else {
resultFolder.append(fullPath).append("/");
}
resultFolder.append("Test_Result_Of_").append(platform);
File file = new File(resultFolder.toString());
if (!file.exists()) {
file = new File(resultFolder.toString());
file.mkdir();
return resultFolder.toString();
} else {
return file.getAbsolutePath();
}
}
/** *//**
* Prints the debug information.
*
* @param writerName the writer name
* @param encoding the encoding
*/
private void printDebugInformation(String writerName, String encoding,
String content) {
StringBuffer msg = new StringBuffer();
msg.append("
<<<<----------------------------------");
msg.append(" Test written by ").append(writerName);
msg.append(" with encoding ").append(encoding);
msg.append(" ---------------------------------->>>>
");
msg.append("
Original string: ").append(content).append("
");
System.out.println(msg.toString());
}
/** *//**
* Reset default system encoding.
*/
private void resetDefaultSystemEncoding() {
System.setProperty("file.encoding", DEFAULT_SYSTEM_ENCODING);
}
}
【1】中国語プラットフォームの場合、テスト結果は以下の通り:
1.FileWriterを使用する場合、GBK符号化を指定します.符号化後の文字長は15で、正常に保存して読み取ることができます.FileWriterを使用する場合、UTF-8の符号化を指定します.符号化後のバイト長は16で、正常に保存して読み取ることができます.FileWriterを使用する場合、ISO 8859-1の符号化を指定します.符号化後のバイト長は17で、正常に保存して読み取ることができます.OutputStreamWriterを使用する場合、GBK符号化を指定します.符号化後の文字長は15で、正常に保存して読み込むことができます.OutputStreamWriterを使用する場合、UTF-8符号化を指定します.符号化後のバイト長は16で、正常に保存して読み込むことができます.OutputStreamWriterを採用し、ISO-8559-1符号化を指定した場合:符号化後バイト長は17になりますか?【2】英語プラットフォームの場合、テスト結果は以下の通り:
1.FileWriterを採用し、GBK符号化を指定した場合:符号化後の文字長は15で、? 2.FileWriterを使用し、UTF-8符号化を指定すると、符号化後のバイト長は16になります. 3.FileWriterを採用し、ISO-8859-1符号化を指定した場合:符号化後バイト長は17で、? 4.OutputStreamWriterを使用する場合、GBK符号化を指定します.符号化後の文字長は15で、正常に保存して読み込むことができます.OutputStreamWriterを使用する場合、UTF-8符号化を指定します.符号化後のバイト長は16で、正常に保存して読み込むことができます.OutputStreamWriterを採用し、ISO-8559-1符号化を指定した場合:符号化後バイト長は17になりますか?【結論】
①中国語プラットフォームでFileWriterを使用すると、文字セットをどのように設定しても機能しません.デフォルトのシステム文字セットを使用しているためです.システムを設置してもsetProperty(「file.encoding」,「ISO-8859-1」)または実行時にパラメータ-Dfileを与える.encoding=UTF-8は機能しません.最終的には「GB 2312」または「GBK」で保存されていることがわかります.中国語プラットフォームでOutputStreamWriterを使用すると、バックグラウンド書き込み時に文字ストリームがバイトストリームに変換され、指定した符号化文字セットが機能します.指定されたGBK、UTF-8の場合、中国語は正常に保存され、読み取ることができ、同時にファイルは私たちが指定した方法で保存されていることがわかります.ISO-8559-1については?これはISO-8859-1を採用して中国語を保存することができないことを再証明して、しかも中国語の符号化がISO-8859-1の符号化の中で対応する文字が見つからないためデフォルトで変換しますか?②英語プラットフォームでFileWriterを使用すると、文字セットをどのように設定しても機能しません.すべてのファイルはISO-8859-1の符号化方式で保存され、間違いなく?になりました.英語プラットフォームでOutputStreamWriterを使用する場合、文字とファイルの符号化方式をGBK、UTF-8に正しく設定した場合にのみ、中国語を正しく保存して表示することができます.
③上記の実験により,クライアントが入力した中国語が異なるプラットフォームで正しく解析,保存,読み取り可能であることが証明された.最善の方法は、OutputStreamWriterを使用してUTF-8符号化することです.UTF-8符号化を使用したくない場合は、GB 2312を使用することが考えられ、GBK、GB 18030は推奨されない.一部の古いテキストエディタでは、GBK、GB 18030の符号化はサポートされていないが、GB 2312ではサポートされている.前はどちらも国標ではないが後者は.
④StringのgetBytes()、getBytes(encoding)、new String(bytes,encoding)の3つの方法について、A.getBytes():プラットフォームのデフォルトの符号化方式(file.encoding属性で取得)を使用して文字列をbyte[]に変換することに注意してください.文字列の最も元のバイト符号化値が得られた.B.getBytes(NAME_OF_CHARSET):指定された符号化方式で文字列をbyte[]に変換し、正しいバイト配列を得るには、プログラマーが正しいNAME_を与えなければならないOF_CHARSET.そうでなければ、正しい結果は得られません.C.new String(bytes,encoding):クライアントでUTF-8でエンコードされたJSPページを使用してリクエストを発行すると、ブラウザでエンコードされたUTF-8バイトがISO-8859-1でサーバ側に渡されます.したがって、HTTPプロトコルによって伝送される元のバイトを得るには、getBytes(「ISO-8859-1」)を呼び出して元のバイトを得る必要があるが、クライアントの元の符号化はUTF-8であるため、ISO-8859-1に従って復号し続けると、中文字ではなく3文字の文字になる.したがって、クライアントの元の文字に復元するには、new String(bytes、「UTF-8」)を再度呼び出し、バイト配列をUTF-8のフォーマットに従って3つのグループごとに復号する必要があります.
D.StringのgetBytes()、getBytes(NAME_OF_CHARSET)の方法はいずれも微妙な方法であり、原則として伝送時にどのような符号化を採用しているのか、この符号化に従ってバイトを得る必要がある.new String(bytes,NAME_OF_CHARSET)は、クライアントがどのような符号化を採用しているのか、原則として、ここのNAME_OF_CHARSETはクライアントと一貫性を保つ必要があります.例えばJSPページがGBKである場合、ページから渡されたパラメータを受信するときはnew String(parameter.getBytes(「ISO-8859-1」)、「GBK」)を使用する必要があります.誤った復号方式を用いてUTF-8を用いた場合,得られるのは文字化けである可能性が高い.つまり、GBK--->ISO-8859-1--->GBK、UTF-8--->ISO-8859-1--->UTF-8の変換過程は問題ありません.しかし、GBK--->ISO-8859-1--->UTF-8、UTF-8--->ISO-8859-1--->GBKのバイトダイレクトトランスコードは、文字化けしてしまう可能性があり、別の変換プロセスが必要です.元の文字符号化と伝送プロトコルで使用されている符号化をよく知らない限り、getBytes(NAME_OF_CHARSET)とnew String(bytes,NAME_OF_CHARSET)は慎重に使用してください.サーバベースの構成、フィルタを使用してrequest/responseのcharacterEncoding、content typeプロパティを設定することを推奨します.あとJSPページのpageEncoding属性、HTML meta要素のcontent type属性です.コード内で文字列を頻繁に符号化しないようにすると、効率が低下し、リスクが増加します.