Mau*_*ice 6 java linux windows png character-encoding
目前我正在尝试以mime格式读取文件,其中包含一些png的二进制字符串数据.
在Windows中,读取文件为我提供了正确的二进制字符串,这意味着我只需复制字符串并将扩展名更改为png即可看到图片.
在Windows中读取文件后的示例如下:
--fh-mms-multipart-next-part-1308191573195-0-53229
Content-Type: image/png;name=app_icon.png
Content-ID: "<app_icon>"
content-location: app_icon.png
‰PNG
Run Code Online (Sandbox Code Playgroud)
等...等...
在Linux中读取文件后的示例如下:
--fh-mms-multipart-next-part-1308191573195-0-53229
Content-Type: image/png;name=app_icon.png
Content-ID: "<app_icon>"
content-location: app_icon.png
�PNG
Run Code Online (Sandbox Code Playgroud)
等...等...
我无法将Linux版本转换成图片,因为它变成了一些时髦的符号,并且有很多颠倒的"?" 和"1/2"符号.
任何人都可以告诉我发生了什么,也许可以提供解决方案?已经玩了一周以上的代码了.
Vin*_*lds 20
�是一个由三个字符组成的序列 - 0xEF 0xBF 0xBD并且是Unicode代码点的UTF-8表示0xFFFD.代码点本身就是非法UTF-8序列的替换字符.
显然,出于某种原因,源代码中涉及的一组例程(在Linux上)正在不准确地处理PNG头.该PNG头与所述字节开始0x89(和后跟0x50,0x4E,0x47),这是在Windows正确处理(其可能被处理该文件作为CP1252的序列字节).在CP1252中,0x89字符显示为‰.
但是,在Linux上,这个字节由UTF-8例程(或者认为将文件作为UTF-8序列处理好的库)解码.因为,它自己的0x89不是ASCII-7范围内的有效代码点(参考:UTF-8编码方案),所以它不能映射到0x00-0x7F范围内的有效UTF-8代码点.此外,它不能映射到表示为多字节UTF-8序列的有效代码点,因为所有多字节序列都以最少2位设置为1(11....)开头,因为这是文件的开头,它也不能是一个连续字节.由此产生的行为是UTF-8解码器现在替换0x89为UTF-8替换字符0xEF 0xBF 0xBD(考虑到文件不是UTF-8开始时有多愚蠢),这将在ISO-8859-1中显示为�.
如果您需要解决此问题,则需要在Linux中确保以下内容:
0xFFFD它实际上是钻石字符 )无法表示,并可能导致进一步的变化(不太可能,但你永远不知道编辑器是怎么做的/ viewer已被写入).*显然,如果将字节序列转换为字符或String对象,Java Runtime将执行将字节序列解码为UTF-16代码点.
在Java中,String≠byte[].
byte[] 表示原始二进制数据.String 表示文本,它具有关联的字符集/编码,以便能够分辨它所代表的字符.二进制数据≠文本.
其中的文本数据String具有Unicode/UTF-16作为字符集/编码(或序列化时为Unicode/mUTF-8).当你从未尝不是一个转换String到一个String或反之亦然,你需要指定非字符集/编码String文本数据(即使你做它含蓄地使用平台的默认字符集).
PNG文件包含表示图像(和关联的元数据)的原始二进制数据,而不是文本.因此,您不应将其视为文本.
\x89PNG它不是文本,它只是用于识别PNG文件的"神奇"标题.0x89甚至没有一个字,它只是一个任意字节值,其唯一理智的表示显示事情一样\x89,0x89......同样,PNG存在在现实中的二进制数据,它可能也已经0xdeadbeef和它会改变什么.PNG恰好是人类可读的事实只是一种便利.
您的问题来自于您的协议混合文本和二进制数据,而Java(与其他语言不同,如C)对二进制数据的处理方式与文本不同.
Java提供*InputStream读取二进制数据和*Reader读取文本.我看到两种处理输入的方法:
String,使用适当的字符集/编码将其转换为a .InputStreamReader之上,当你想要二进制数据时直接InputStream访问InputStream,访问InputStreamReader你想要的文本.你可能想要缓冲,把它放在第二种情况下的正确位置是在*Reader.如果你使用了a BufferedReader,BufferedReader那么它可能会消耗更多的输入InputStream.所以,你会有类似的东西:
?????????????????????
? InputStreamReader ?
?????????????????????
?
???????????????????????
? BufferedInputStream ?
???????????????????????
?
???????????????
? InputStream ?
???????????????
Run Code Online (Sandbox Code Playgroud)
您将使用InputStreamReader读取文本,然后您将使用BufferedInputStream从同一流中读取适当数量的二进制数据.
一个有问题的案例是将"\r"(旧的MacOS)和"\r\n"(DOS/Windows)都识别为行终止符.在这种情况下,你最终可能会过多地阅读一个角色.您可以采用弃用DataInputStream.readline()方法所采用的方法:透明地将内部包装InputStream到PushbackInputStream未读取的字符中.
但是,由于您似乎没有Content-Length,我建议采用第一种方式,将所有内容视为二进制,并在String只读完一行后转换为.在这种情况下,我会将MIME分隔符视为二进制数据.
输出:
由于您正在处理二进制数据,因此您不能只使用println()它.PrintStream具有write()可以处理二进制数据的方法(例如:用于输出到二进制文件).
或者,您的数据可能必须在将其视为文本的渠道上传输.Base64专为这种情况而设计(将二进制数据传输为ASCII文本).Base64编码形式仅使用US_ASCII字符,因此您应该能够将其与任何作为US_ASCII(ISO-8859-*,UTF-8,CP-1252,...)的超集的字符集/编码一起使用.由于您要将二进制数据转换为/从文本转换,因此Base64唯一合理的API就是:
String Base64Encode(byte[] data);
byte[] Base64Decode(String encodedData);
Run Code Online (Sandbox Code Playgroud)
这基本上是内部java.util.prefs.Base64使用的.
结论:
在Java中,String≠byte[].
二进制数据≠文本.
| 归档时间: |
|
| 查看次数: |
12357 次 |
| 最近记录: |