欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用FileReader采用的默認(rèn)編碼

 更新時(shí)間:2021年12月10日 09:00:28   作者:知了只有一夏  
這篇文章主要介紹了使用FileReader采用的默認(rèn)編碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

FileReader采用的默認(rèn)編碼

很久以前聽(tīng)教學(xué)視頻,里面講到Java采用的默認(rèn)編碼是ISO-8859-1,一直記著。

但是最近重新看IO流的時(shí)候,驚訝地發(fā)現(xiàn),在不指定字符編碼的情況下,F(xiàn)ileReader居然可以讀取內(nèi)容為中文的文本文件。要知道ISO-8859-1可是西歐字符集,怎么能包含中文呢?于是百度了一下關(guān)鍵詞“IOS-8859-1顯示中文”,結(jié)果很多人都有這個(gè)疑惑。

代碼如下:

package day170903; 
import java.io.*; 
public class TestDecoder {
	public static void main(String[] args) {
		FileReader fr = null;
		try {
			fr = new FileReader("G:/io/hello.txt");
			int len = 0;
			while((len=fr.read())!=-1) {
				System.out.println((char)len);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(fr!=null) {
					fr.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

事情的真相是什么呢?

編碼一般是在構(gòu)造方法處指定的,于是查看一下FileReader的構(gòu)造方法。也是奇葩,以前沒(méi)怎么注意過(guò),F(xiàn)ileReader竟然沒(méi)有可以指定字符編碼的構(gòu)造方法。而且僅僅是簡(jiǎn)單地從InputStreamReader繼承,并沒(méi)有重寫(xiě)或擴(kuò)展任何方法。這可能是歷史上最吝嗇的子類,完全就是啃老族。

不過(guò)好在Java的文檔注釋寫(xiě)得很給力,在FileReader這個(gè)類的開(kāi)頭有下面一段文檔注釋(中文部分為我劣質(zhì)的翻譯):

/**
 * Convenience class for reading character files.  The constructors of this
 * class assume that the default character encoding and the default byte-buffer
 * size are appropriate.  To specify these values yourself, construct an
 * InputStreamReader on a FileInputStream.
 *
 *這是一個(gè)很方便的讀取字符文件(文本文件)的類。
 *這個(gè)類的構(gòu)造方法假設(shè)默認(rèn)的字符編碼和默認(rèn)的緩存數(shù)組大小是合適的(滿足需要的)。
 *假如你想自己指定字符編碼和緩存數(shù)組的大小,
 *請(qǐng)使用基于FileInputStream的InputStreamReader類。
 * <p><code>FileReader</code> is meant for reading streams of characters.
 * For reading streams of raw bytes, consider using a
 * <code>FileInputStream</code>.
 *
 *FileReader是設(shè)計(jì)為用來(lái)讀取字符流的。
 *想要讀取原始的字節(jié)流的話,可以考慮使用FileInputStream
 * @see InputStreamReader
 * @see FileInputStream
 *
 * @author      Mark Reinhold
 * @since       JDK1.1
 */

所以,設(shè)計(jì)者已經(jīng)在文檔注釋中講明白了這么設(shè)計(jì)的原因。但是對(duì)于我們來(lái)說(shuō),現(xiàn)在比較重要的是這個(gè)所謂的默認(rèn)的字符編碼是什么。

這個(gè)時(shí)候我們來(lái)看一下我們使用的FileReader中的那個(gè)構(gòu)造方法的具體內(nèi)容。

    public FileReader(String fileName) throws FileNotFoundException {
        super(new FileInputStream(fileName));
    }

FileReader繼承自InputStreamReader,調(diào)用了InputStreamReader的接受InputStream類型的形參的構(gòu)造方法,也就是下面這個(gè)。

    public InputStreamReader(InputStream in) {
        super(in);
        try {
            sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
        } catch (UnsupportedEncodingException e) {
            // The default encoding should always be available
            throw new Error(e);
        }
    }

當(dāng)然InputStreamReader的這個(gè)構(gòu)造方法又調(diào)用了其父類Reader的下面的構(gòu)造方法。

    protected Reader(Object lock) {
        if (lock == null) {
            throw new NullPointerException();
        }
        this.lock = lock;
    }

在這里,它只是把得到的InputStream對(duì)象賦值給成員變量lock(看lock這個(gè)成員變量的文檔注釋的話,大概知道它是用來(lái)保證同步的),并沒(méi)有說(shuō)到字符編碼的事。

既然通過(guò)super(in)向上查找到父類Reader的構(gòu)造方法也沒(méi)有發(fā)現(xiàn)默認(rèn)字符編碼的蹤跡,那么這條道就到頭了。接下來(lái)應(yīng)該看的是super(in)下面的代碼,也就是那個(gè)異常捕捉語(yǔ)句塊。主體語(yǔ)句只有下面一行內(nèi)容。

sd = StreamDecoder.forInputStreamReader(in, this, (String)null);

仔細(xì)看FileReader和其它IO流的代碼的話會(huì)發(fā)現(xiàn),很多輸入流的讀取功能(read及其重載方法)都是通過(guò)這個(gè)StreamDecoder完成的,這是后話。在Eclipse里面直接查看這個(gè)

StreamDecoder的源碼是不行的,需要去openjdk上找。

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/nio/cs/StreamDecoder.java

上面異常捕捉語(yǔ)句塊主體部分調(diào)用的是StreamDecoder的forInputStreamReader方法,對(duì)應(yīng)的代碼如下:

public static StreamDecoder forInputStreamReader(InputStream in,
                                                 Object lock,
                                                 String charsetName)
    throws UnsupportedEncodingException
{
    String csn = charsetName;
    if (csn == null)
        csn = Charset.defaultCharset().name();
    try {
        if (Charset.isSupported(csn))
            return new StreamDecoder(in, lock, Charset.forName(csn));
    } catch (IllegalCharsetNameException x) { }
    throw new UnsupportedEncodingException (csn);
}

其實(shí)調(diào)用的時(shí)候,傳遞的第三個(gè)參數(shù)是字符串形式的null,這個(gè)其實(shí)就是我們要找的默認(rèn)字符編碼。

我們要找的是默認(rèn)字符編碼,其它代碼不必深究。第一行是說(shuō)把接收到的第三個(gè)參數(shù)賦值給csn(局部變量:字符編碼),當(dāng)然了,這個(gè)是被InputStreamReader的帶字符編碼參數(shù)的構(gòu)造方法調(diào)用的時(shí)候才有意義的。沒(méi)有指定字符編碼的構(gòu)造方法調(diào)用StreamDecoder的forInputStreamReader的時(shí)候傳遞是null。所以接下來(lái)的if語(yǔ)句判斷就成立了,那么csn這個(gè)變量得到的就是Charset.defaultCharset().name(),見(jiàn)名知意,即默認(rèn)字符編碼。

接下來(lái)就要看Charset這個(gè)類的defaultCharset方法的返回值——Charset對(duì)象的name()方法的返回值是什么了。說(shuō)起來(lái)有點(diǎn)繞,其實(shí)就是找里面的默認(rèn)字符編碼。

public static Charset defaultCharset() {
    if (defaultCharset == null) {
        synchronized (Charset.class) {
            String csn = AccessController.doPrivileged(
                new GetPropertyAction("file.encoding"));
            Charset cs = lookup(csn);
            if (cs != null)
                defaultCharset = cs;
            else
                defaultCharset = forName("UTF-8");
        }
    }
    return defaultCharset;
}

這代碼看起來(lái)很費(fèi)勁,而且接著又要看其它代碼。最終結(jié)果是這個(gè)所謂的默認(rèn)字符編碼,其實(shí)就是JVM啟動(dòng)時(shí)候的本地編碼。

這個(gè)要查看的話,就在對(duì)應(yīng)的項(xiàng)目上點(diǎn)擊右鍵,選擇Properties選項(xiàng),在彈出的屬性窗口中,可以看到當(dāng)前項(xiàng)目在JVM中運(yùn)行時(shí)候的默認(rèn)字符編碼。對(duì)于咱們中國(guó)人來(lái)說(shuō),一般都是“GBK”,不過(guò)可以根據(jù)需要從下拉框選擇。

這代碼看起來(lái)很費(fèi)勁,而且接著又要看其它代碼。最終結(jié)果是這個(gè)所謂的默認(rèn)字符編碼,其實(shí)就是JVM啟動(dòng)時(shí)候的本地編碼。

這個(gè)要查看的話,就在對(duì)應(yīng)的項(xiàng)目上點(diǎn)擊右鍵,選擇Properties選項(xiàng),在彈出的屬性窗口中,可以看到當(dāng)前項(xiàng)目在JVM中運(yùn)行時(shí)候的默認(rèn)字符編碼。對(duì)于咱們中國(guó)人來(lái)說(shuō),一般都是“GBK”,不過(guò)可以根據(jù)需要從下拉框選擇。

所以開(kāi)頭那個(gè)疑問(wèn),完全是因?yàn)椴恢滥J(rèn)的編碼其實(shí)是GBK而產(chǎn)生的誤解。反過(guò)來(lái)測(cè)試一下就好了,先用OutputStreamWriter往文件中寫(xiě)入下面一句法語(yǔ)

Est-ce possible que tu sois en train de penser à moi lorsque tu me manques?

我在想你的時(shí)候,你會(huì)不會(huì)也剛好正在想我?

寫(xiě)入的時(shí)候指定字符編碼為ISO-8859-1,然后用InputStreamReader讀取,讀取的時(shí)候不指定字符編碼(即采用默認(rèn)字符編碼)。那么,假如不能正確還原這句話,就說(shuō)明默認(rèn)的字符編碼并不是ISO-8859-1。

package day170903; 
import java.io.*; 
public class TestDefaultCharEncoding {
	public static void main(String[] args) {
		InputStreamReader isr = null;
		OutputStreamWriter osw = null;
		try {
			osw = new OutputStreamWriter(new FileOutputStream("G:/io/ISO-8859-1.txt"),"ISO-8859-1");
			isr = new InputStreamReader(new FileInputStream("G:/io/ISO-8859-1.txt"));
			char[] chars = "Est-ce possible que tu sois en train de penser à moi lorsque tu me manques?".toCharArray();
			osw.write(chars);
			osw.flush();
			int len = 0;
			while((len=isr.read())!=-1) {
				System.out.print((char)len);
			}
		} catch (UnsupportedEncodingException | FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(isr!=null) {
					isr.close();
				}
				if(osw!=null) {
					osw.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

輸出結(jié)果是:

Est-ce possible que tu sois en train de penser ? moi lorsque tu me manques?

大部分都正確還原了,因?yàn)榉ㄕZ(yǔ)中大部分也是英文字母。但是那個(gè)法語(yǔ)特有的(相比于英語(yǔ))à 讀出來(lái)以后無(wú)法識(shí)別,變成了問(wèn)號(hào)。

假如默認(rèn)編碼真的是ISO-8859-1,那么讀取是完全沒(méi)有問(wèn)題的?,F(xiàn)在有問(wèn)題,正好說(shuō)明默認(rèn)編碼不是ISO-8859-1。

基本上到這兒就完事了,但是還要說(shuō)一句。雖然我們可以很方便地知道在不指定字符編碼的情況下,JVM將會(huì)采用什么編碼,但是還是建議采用字符類的時(shí)候加上字符編碼,因?yàn)閷?xiě)清楚字符編碼可以讓別人明白你的原意,而且能避免代碼轉(zhuǎn)手后換了一個(gè)開(kāi)發(fā)工具后可能出現(xiàn)的編碼異常問(wèn)題。

FileReader的編碼問(wèn)題

有一個(gè)UTF-8編碼的文本文件,用FileReader讀取到一個(gè)字符串,然后轉(zhuǎn)換字符集:str=new String(str.getBytes(),"UTF-8");結(jié)果大部分中文顯示正常,但最后仍有部分漢字顯示為問(wèn)號(hào)!

public static List<String> getLines( String fileName )  
{  
    List<String> lines = new ArrayList<String>();  
    try  
    {  
        BufferedReader br = new BufferedReader(new FileReader(fileName));  
        String line = null;  
        while( ( line = br.readLine() ) != null )  
            lines.add(new String(line.getBytes("GBK"), "UTF-8"));  
        br.close();  
    }  
    catch( FileNotFoundException e )  
    {  
    }  
    catch( IOException e )  
    {  
    }  
    return lines;  
}  

文件讀入時(shí)是按OS的默認(rèn)字符集即GBK解碼的,我先用默認(rèn)字符集GBK編碼str.getBytes(“GBK”),此時(shí)應(yīng)該還原為文件中的字節(jié)序列了,然后再按UTF-8解碼,生成的字符串按理說(shuō)應(yīng)該就應(yīng)該是正確的。

為什么結(jié)果中還是有部分亂碼呢?

問(wèn)題出在FileReader讀取文件的過(guò)程中,F(xiàn)ileReader繼承了InputStreamReader,但并沒(méi)有實(shí)現(xiàn)父類中帶字符集參數(shù)的構(gòu)造函數(shù),所以FileReader只能按系統(tǒng)默認(rèn)的字符集來(lái)解碼,然后在UTF-8 -> GBK -> UTF-8的過(guò)程中編碼出現(xiàn)損失,造成結(jié)果不能還原最初的字符。

原因明確了,用InputStreamReader代替FileReader,InputStreamReader isr=new InputStreamReader(new FileInputStream(fileName),"UTF-8");這樣讀取文件就會(huì)直接用UTF-8解碼,不用再做編碼轉(zhuǎn)換。

public static List<String> getLines( String fileName )  
{  
    List<String> lines = new ArrayList<String>();  
    try  
    {  
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), "UTF-8"));  
        String line = null;  
        while( ( line = br.readLine() ) != null )  
            lines.add(line);  
        br.close();  
    }  
    catch( FileNotFoundException e )  
    {  
    }  
    catch( IOException e )  
    {  
    }  
    return lines;  
}  

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • java實(shí)現(xiàn)一個(gè)桌球小游戲

    java實(shí)現(xiàn)一個(gè)桌球小游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)一個(gè)桌球小游戲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • RabbitMQ中的channel信道、exchange交換機(jī)和queue隊(duì)列詳解

    RabbitMQ中的channel信道、exchange交換機(jī)和queue隊(duì)列詳解

    這篇文章主要介紹了RabbitMQ中的channel信道、exchange交換機(jī)和queue隊(duì)列詳解,connection是指物理的連接,一個(gè)client與一個(gè)server之間有一個(gè)連接,一個(gè)連接上可以建立多個(gè)channel,可以理解為邏輯上的連接,需要的朋友可以參考下
    2023-08-08
  • Java數(shù)據(jù)結(jié)構(gòu)之復(fù)雜度篇

    Java數(shù)據(jù)結(jié)構(gòu)之復(fù)雜度篇

    算法復(fù)雜度分為時(shí)間復(fù)雜度和空間復(fù)雜度。其作用:?時(shí)間復(fù)雜度是度量算法執(zhí)行的時(shí)間長(zhǎng)短;而空間復(fù)雜度是度量算法所需存儲(chǔ)空間的大小
    2022-01-01
  • JVM調(diào)試命令與調(diào)試工具詳解

    JVM調(diào)試命令與調(diào)試工具詳解

    JVM statistics Monitoring,用于監(jiān)視虛擬機(jī)運(yùn)行時(shí)狀態(tài)信息的命令,它可以顯示出虛擬機(jī)進(jìn)程中的類裝載、內(nèi)存、垃圾收集、JIT編譯等運(yùn)行數(shù)據(jù),這篇文章主要介紹了JVM調(diào)試命令與調(diào)試工具,需要的朋友可以參考下
    2023-10-10
  • 最新評(píng)論