java字节流非英语字符

zy1mlcev  于 2021-07-06  发布在  Java
关注(0)|答案(3)|浏览(343)

我读过这个密码。因为xanadu.txt的内容使用“test”。文件大小为4字节。如果我使用debug运行 out.write(c) 每次打开文件outagain.txt(使用记事本)一个字节,然后依次看到:t-->te-->tes-->test。好的,但是如果我们把源文件(xanadu.txt)的内容改成希腊语(或其他语言)就相当于test(τέστ) 然后文件现在有8个字节的大小(我想因为utf我们每个字符有2个字节)。再次调试时,每次都会出现无意义的象形文字 out.write(c) 跑。当最后一个字节(第8个)打印出来时,原来的希腊字突然出现了(τέστ) 出现。为什么?如果我们选择控制台流作为目标(在netbeans中)也是一样的,但是在这种情况下,如果debug,奇怪的字符会保留在末尾,但是如果我们正常运行它就不会了(!!!)。

iq0todco

iq0todco1#

正如您所观察到的,单个字符(java内部表示法中的16位)在字节流表示法中变成了可变的字节数,特别是utf-8。
(有些字符占用两个字符值;我将忽略这些,但答案仍然适用,只是更加适用)
如果像实验中那样输出“按字节”,在某些情况下会输出一个小数字符。这是一个非法的序列,毫无意义;一些软件(如记事本)仍然会试图理解它。这甚至可能包括猜测编码。例如,我不知道是这样的,但是如果文件的前几个字节不是有效的utf-8——我们知道你的半个字符的输出不是有效的utf-8——那么记事本可能会猜测一种完全不同的编码,它会将字节序列视为完全不同字符的有效表示。
热释光;博士-垃圾出来,垃圾展示。

uyto3xhc

uyto3xhc2#

热释光;博士

阅读:绝对最低限度每个软件开发人员绝对,肯定必须了解unicode和字符集(没有借口!)
不要按八位字节(字节)分析文本文件。使用专门为处理文本而构建的类。例如,使用 Files 以及它的 readAllLines 方法。

详细信息

请注意,在该教程页面的底部,警告这不是处理文本文件的正确方法:
copybytes看起来像一个普通的程序,但实际上它代表了一种低级别的i/o,您应该避免使用。因为xanadu.txt包含字符数据,所以最好的方法是使用字符流,这将在下一节中讨论。
文本文件可以或不可以使用单个八位字节来表示单个字符,例如美国ascii文件。您的示例代码假定每个字符有一个八位字节,这适用于 test 作为内容而不是 τέστ 作为内容。
作为一名程序员,您必须从数据文件的发布者那里知道在编写表示原始文本的数据时使用了什么编码。一般来说,在编写文本时最好使用utf-8编码。
编写一个包含两行的文本文件:
试验τέστ
…并使用编码为utf-8的文本编辑器保存。
将该文件作为 String 物体。

Path path = Paths.get( "/Users/basilbourque/some_text.txt" );
try
{
    List < String > lines = Files.readAllLines( path , StandardCharsets.UTF_8 );
    for ( String line : lines )
    {
        System.out.println( "line = " + line );
    }
}
catch ( IOException e )
{
    e.printStackTrace();
}

运行时:

line = test
line = τέστ

utf-16与utf-8

你说:
我想因为utf我们每个字符有2个字节)
没有所谓的“utf”。
utf-16编码每个字符使用一对或多对八位字节。
utf-8编码每个字符使用1、2、3或4个八位字节。
文本内容,例如 τέστ 可以使用utf-16或utf-8编码写入文件。请注意,utf-16“被认为是有害的”,而utf-8现在通常是首选的。注意,utf-8是usascii的超集,因此任何usascii文件也是utf-8文件。

字符作为代码点

如果要对文本中的每个字符进行示例,请将它们视为代码点号。
永远不要使用 char 键入java。这种类型甚至不能表示unicode中定义的一半字符,现在已经过时了。
我们可以通过添加这两行代码来查询上述示例文件中的每个字符。

IntStream codePoints = line.codePoints();
codePoints.forEach( System.out :: println );

这样地:

Path path = Paths.get( "/Users/basilbourque/some_text.txt" );
try
{
    List < String > lines = Files.readAllLines( path , StandardCharsets.UTF_8 );
    for ( String line : lines )
    {
        System.out.println( "line = " + line );
        IntStream codePoints = line.codePoints();
        codePoints.forEach( System.out :: println );
    }
}
catch ( IOException e )
{
    e.printStackTrace();
}

运行时:

line = test
116
101
115
116
line = τέστ
964
941
963
964

如果您还不熟悉流,请转换 IntStream 收藏,如 ListInteger 物体。

Path path = Paths.get( "/Users/basilbourque/some_text.txt" );
try
{
    List < String > lines = Files.readAllLines( path , StandardCharsets.UTF_8 );
    for ( String line : lines )
    {
        System.out.println( "line = " + line );
        List < Integer > codePoints = line.codePoints().boxed().collect( Collectors.toList() );
        for ( Integer codePoint : codePoints )
        {
            System.out.println( "codePoint = " + codePoint );
        }
    }
}
catch ( IOException e )
{
    e.printStackTrace();
}

运行时:

line = test
codePoint = 116
codePoint = 101
codePoint = 115
codePoint = 116
line = τέστ
codePoint = 964
codePoint = 941
codePoint = 963
codePoint = 964

给定一个码位号,我们就可以确定想要的字符。
字符串s=character.tostring(941);//έ 性格。
请注意,某些文本字符可能表示为多个代码点,例如带有变音符号的字母(文本处理不是一件简单的事情。)

pxiryf3j

pxiryf3j3#

现代计算机有一个巨大的表格,里面有40亿个字符。每个字符由一个32位数字标识。你能想到的所有人物都在这里;从基本的“测试”到τέστ' 雪人(☃ ), 一些特殊的不可见的词表示从右到左拼写的单词即将出现,一些连字(例如ff - 哪一个字符代表ff连字)到表情符号、彩色和全部:?。
整个答案基本上是这些32位数字的序列。但是你想如何将这些存储在一个文件中呢?这就是“编码”的用武之地。有很多很多编码,一个关键的问题是(几乎)没有编码是“可检测的”。
是这样的:
如果一个完全陌生的人走到你跟前说“嘿!”,他们说什么语言?可能是英国人。但也许是荷兰语,也有“嘿!”。也可能是日本人,他们甚至不问候你,他们说'是'(或多或少)。你怎么知道?
答案是,要么从外部语境(如果你在英国纽卡斯尔的中部,可能是英语),要么因为他们明确告诉你,但一个是外部的,另一个不是常见的做法。
文本文件也是这样。
它们只包含编码的文本,并不表示它是什么编码。这意味着你需要告诉编辑,或者 newBufferedReader 在java中,或在浏览器中保存txt内容时,需要什么编码。但是,因为每次都要这么做很烦人,所以大多数系统都有一个默认的选择。一些文本编辑器甚至试图找出它是什么编码,但就像那个人说“嘿!”对你来说,可能是英语或日语,有着截然不同的解释,同样的情况也发生在这种半智能的字符集编码猜测上。
这让我们得到以下解释:
你在写什么 τέστ 在编辑器中单击“保存”。你的编辑在做什么?它在utf-16中节省吗?utf-8?ucs-4?iso-8859-7?所有这些编码都会产生完全不同的文件!假设它有8个字节,这意味着它是utf-16或utf-8。可能是utf-8。
然后逐个复制这些字节,这是有问题的:在utf-8中,一个字节可以是一个字符的一半(您说过:utf-8将字符存储为2字节;这不是真的,utf-8存储字符,使得每个字符都是1、2、3或4字节;它是每个字节的可变长度中的每个字符τέστ 这意味着如果你复制了,比如说,3个字节,你的文本编辑器猜测它可能是什么的能力就会受到严重的阻碍:它可能会猜测utf-8,但后来意识到它根本不是有效的utf-8(因为你最后得到的是半个字符),所以它猜错了,并显示你狼吞虎咽。
这里要吸取的教训有两个:
如果要处理字符,请使用 char , Reader , Writer , String ,以及其他以性格为导向的东西。
如果要处理字节,请使用 byte , byte[] , InputStream , OutputStream ,以及其他面向字节的内容。
千万不要误以为这两个很容易互换,因为它们不是。无论何时从一个“世界”转到另一个“世界”,都必须指定字符集编码,因为如果不指定,java会选择“平台默认值”,这是您不需要的(因为现在您的软件依赖于外部因素,无法测试)。伊克斯)。
默认为utf-8。

相关问题