溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java中的字符串和編碼舉例分析

發(fā)布時間:2021-11-24 16:08:32 來源:億速云 閱讀:111 作者:iii 欄目:大數(shù)據(jù)

本篇內(nèi)容介紹了“java中的字符串和編碼舉例分析”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

使用變長編碼的不完全字符來創(chuàng)建字符串

在java中String的底層存儲char[]是以UTF-16進行編碼的。

注意,在JDK9之后,String的底層存儲已經(jīng)變成了byte[]。

StringBuilder和StringBuffer還是使用的是char[]。

那么當我們在使用InputStreamReader,OutputStreamWriter和String類進行String讀寫和構建的時候,就需要涉及到UTF-16和其他編碼的轉換。

我們來看一下從UTF-8轉換到UTF-16可能會遇到的問題。

先看一下UTF-8的編碼:

java中的字符串和編碼舉例分析

UTF-8使用1到4個字節(jié)表示對應的字符,而UTF-16使用2個或者4個字節(jié)來表示對應的字符。

轉換起來可能會出現(xiàn)什么問題呢?

    public String readByteWrong(InputStream inputStream) throws IOException {
        byte[] data = new byte[1024];
        int offset = 0;
        int bytesRead = 0;
        String str="";

        while ((bytesRead = inputStream.read(data, offset, data.length - offset)) != -1) {
            str += new String(data, offset, bytesRead, "UTF-8");
            offset += bytesRead;
            if (offset >= data.length) {
                throw new IOException("Too much input");
            }
        }
        return str;
    }

上面的代碼中,我們從Stream中讀取byte,每讀一次byte就將其轉換成為String。很明顯,UTF-8是變長的編碼,如果讀取byte的過程中,恰好讀取了部分UTF-8的代碼,那么構建出來的String將是錯誤的。

我們需要下面這樣操作:

    public String readByteCorrect(InputStream inputStream) throws IOException {
        Reader r = new InputStreamReader(inputStream, "UTF-8");
        char[] data = new char[1024];
        int offset = 0;
        int charRead = 0;
        String str="";

        while ((charRead = r.read(data, offset, data.length - offset)) != -1) {
            str += new String(data, offset, charRead);
            offset += charRead;
            if (offset >= data.length) {
                throw new IOException("Too much input");
            }
        }
        return str;
    }

我們使用了InputStreamReader,reader將會自動把讀取的數(shù)據(jù)轉換成為char,也就是說自動進行UTF-8到UTF-16的轉換。

所以不會出現(xiàn)問題。

char不能表示所有的Unicode

因為char是使用UTF-16來進行編碼的,對于UTF-16來說,U+0000 to U+D7FF 和 U+E000 to U+FFFF,這個范圍的字符,可以直接用一個char來表示。

但是對于U+010000 to U+10FFFF是使用兩個0xD800–0xDBFF和0xDC00–0xDFFF范圍的char來表示的。

這種情況下,兩個char合并起來才有意思,單獨一個char是沒有任何意義的。

考慮下面的我們的的一個subString的方法,該方法的本意是從輸入的字符串中找到第一個非字母的位置,然后進行字符串截取。

public static String subStringWrong(String string) {
        char ch;
        int i;
        for (i = 0; i < string.length(); i += 1) {
            ch = string.charAt(i);
            if (!Character.isLetter(ch)) {
                break;
            }
        }
        return string.substring(i);
    }

上面的例子中,我們一個一個的取出string中的char字符進行比較。如果遇到U+010000 to U+10FFFF范圍的字符,就可能報錯,誤以為該字符不是letter。

我們可以這樣修改:

public static String subStringCorrect(String string) {
        int ch;
        int i;
        for (i = 0; i < string.length(); i += Character.charCount(ch)) {
            ch = string.codePointAt(i);
            if (!Character.isLetter(ch)) {
                break;
            }
        }
        return string.substring(i);
    }

我們使用string的codePointAt方法,來返回字符串的Unicode code point,然后使用該code point來進行isLetter的判斷就好了。

注意Locale的使用

為了實現(xiàn)國際化支持,java引入了Locale的概念,而因為有了Locale,所以會導致字符串在進行轉換的過程中,產(chǎn)生意想不到變化。

考慮下面的例子:

    public void toUpperCaseWrong(String input){
        if(input.toUpperCase().equals("JOKER")){
            System.out.println("match!");
        }
    }

我們期望的是英語,如果系統(tǒng)設置了Locale是其他語種的話,input.toUpperCase()可能得到完全不一樣的結果。

幸好,toUpperCase提供了一個locale的參數(shù),我們可以這樣修改:

    public void toUpperCaseRight(String input){
        if(input.toUpperCase(Locale.ENGLISH).equals("JOKER")){
            System.out.println("match!");
        }
    }

同樣的, DateFormat也存在著問題:

    public void getDateInstanceWrong(Date date){
        String myString = DateFormat.getDateInstance().format(date);
    }

    public void getDateInstanceRight(Date date){
        String myString = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.US).format(date);
    }

我們在進行字符串比較的時候,一定要考慮到Locale影響。

文件讀寫中的編碼格式

我們在使用InputStream和OutputStream進行文件對寫的時候,因為是二進制,所以不存在編碼轉換的問題。

但是如果我們使用Reader和Writer來進行文件的對象,就需要考慮到文件編碼的問題。

如果文件是UTF-8編碼的,我們是用UTF-16來讀取,肯定會出問題。

考慮下面的例子:

    public void fileOperationWrong(String inputFile,String outputFile) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader(inputFile));
        PrintWriter writer = new PrintWriter(new FileWriter(outputFile));
        int line = 0;
        while (reader.ready()) {
            line++;
            writer.println(line + ": " + reader.readLine());
        }
        reader.close();
        writer.close();
    }

我們希望讀取源文件,然后插入行號到新的文件中,但是我們并沒有考慮到編碼的問題,所以可能會失敗。

上面的代碼我們可以修改成這樣:

BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), Charset.forName("UTF8")));
PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(outputFile), Charset.forName("UTF8")));

通過強制指定編碼格式,從而保證了操作的正確性。

不要將非字符數(shù)據(jù)編碼為字符串

我們經(jīng)常會有這樣的需求,就是將二進制數(shù)據(jù)編碼成為字符串String,然后存儲在數(shù)據(jù)庫中。

二進制是以Byte來表示的,但是從我們上面的介紹可以得知不是所有的Byte都可以表示成為字符。如果將不能表示為字符的Byte進行字符的轉化,就有可能出現(xiàn)問題。

看下面的例子:

    public void convertBigIntegerWrong(){
        BigInteger x = new BigInteger("1234567891011");
        System.out.println(x);
        byte[] byteArray = x.toByteArray();
        String s = new String(byteArray);
        byteArray = s.getBytes();
        x = new BigInteger(byteArray);
        System.out.println(x);
    }

上面的例子中,我們將BigInteger轉換為byte數(shù)字(大端序列),然后再將byte數(shù)字轉換成為String。最后再將String轉換成為BigInteger。

先看下結果:

1234567891011
80908592843917379

發(fā)現(xiàn)沒有轉換成功。

雖然String可以接收第二個參數(shù),傳入字符編碼,目前java支持的字符編碼是:ASCII,ISO-8859-1,UTF-8,UTF-8BE, UTF-8LE,UTF-16,這幾種。默認情況下String也是大端序列的。

上面的例子怎么修改呢?

    public void convertBigIntegerRight(){
        BigInteger x = new BigInteger("1234567891011");
        String s = x.toString();  //轉換成為可以存儲的字符串
        byte[] byteArray = s.getBytes();
        String ns = new String(byteArray);
        x = new BigInteger(ns);
        System.out.println(x);
    }

我們可以先將BigInteger用toString方法轉換成為可以表示的字符串,然后再進行轉換即可。

我們還可以使用Base64來對Byte數(shù)組進行編碼,從而不丟失任何字符,如下所示:

    public void convertBigIntegerWithBase64(){
        BigInteger x = new BigInteger("1234567891011");
        byte[] byteArray = x.toByteArray();
        String s = Base64.getEncoder().encodeToString(byteArray);
        byteArray = Base64.getDecoder().decode(s);
        x = new BigInteger(byteArray);
        System.out.println(x);

    }

“java中的字符串和編碼舉例分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注億速云網(wǎng)站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI