在Java中如何将文件作为无符号字节读取?

8

如何在Java中将文件读取为字节?

需要注意的是,所有字节都需要是正数,即不能使用负范围。

在Java中是否可以实现此操作,如果可以,如何实现?

我需要能够将文件内容乘以一个常数。我假设可以将字节读入BigInteger然后进行乘法运算,但由于某些字节为负数,我最终得到12 13 15 -12等结果并且无法继续操作。


对象DataInputStream提供了readUnsignedByte()方法。 - Adrian Grygutis
5个回答

19

嗯,Java 没有无符号字节的概念......byte 类型始终是带符号的,其值范围从 -128 到 127(含)。然而,这将与其他已使用无符号值的系统很好地进行交互,例如,C# 代码写入一个字节的“255”将生成一个文件,在 Java 中读取相同的值为“-1”。只要小心谨慎,就不会出问题。

编辑:您可以使用掩码非常轻松地将带符号字节转换为具有无符号值的 int。例如:

byte b = -1; // Imagine this was read from the file
int i = b & 0xff;
System.out.println(i); // 255

在所有算术运算中使用int,然后在需要重新写出时将其转换回byte

通常使用FileInputStream或可能是FileChannel从文件中读取二进制数据。

目前很难知道您还在寻找什么其他内容...如果您在问题中提供更多细节,我们可能能够更好地帮助您。


我需要能够将文件的内容乘以一个常数。我原本以为可以将字节读入BigInteger中,然后进行乘法运算,但由于一些字节是负数,所以最终得到了12 13 15 -12等结果,导致程序无法继续执行。 - tyr
1
@tyr 你是逐个字节进行乘法运算,还是基于读取的几个字节构建一个BigInteger?如果是后者,我不明白为什么你要将字节转换为它们的无符号表示形式。 - someguy
我正在根据读取的字节构建一个BigInteger。我进行转换是因为当我使用BigInteger "12-1213"作为参数调用方法时,会生成异常java.lang.NumberFormatException:非法嵌入负号。 - tyr
读取的字节是在BigInteger中简单地串联在一起的,一个接一个的字节。 - tyr
@tyr 啊,你传递的是一个字符串。我以为你是指你将传递一个字节数组来构造 BigInteger。抱歉。 - someguy

2
使用Java 8中的unsigned API,你可以使用Byte.toUnsignedInt函数。这比手动转换和掩码处理要更加简洁。
当然,在对它进行操作后将int类型转换为byte类型只需要进行强制类型转换(byte)value即可。

1

如果在内部使用更大的整数类型不是问题,那么就选择简单的解决方案,在将它们相乘之前将所有整数加上128。这样,你可以得到0到255而不是-128到127。加法并不困难 ;)

此外,请记住Java中的算术和位运算符仅返回整数,因此:

byte a = 0;
byte b = 1;

byte c = a | b;

会产生编译时错误,因为 | 运算符返回的是整型。你需要这样做:

byte c = (byte) a | b;

所以我建议在将数字相乘之前,将它们全部加上128。


1
假设他想要从二进制补码转换为无符号数,那是行不通的。例如,-1 + 128 的结果是127而不是应该的255。此外,将其强制转换回字节会改变超出字节范围的任何值。 - someguy

1

您在评论中写道(请将此类信息放入问题中 - 有一个编辑链接可供使用):

我需要能够将文件的内容乘以一个常数。 我假设我可以将字节读入BigInteger,然后进行乘法运算, 但由于某些字节是负数,我最终得到12 13 15 -12等结果并卡住了。

如果要将整个文件用作BigInteger,请将其读入byte []中,并将此数组(作为整体)提供给BigInteger构造函数。

/**
 * reads a file and converts the content to a BigInteger.
 * @param f the file name. The content is interpreted as
 *   big-endian base-256 number.
 * @param signed if true, interpret the file's content as two's complement
 *                  representation of a signed number.
 *               if false, interpret the file's content as a unsigned
 *                  (nonnegative) number.
 */
public static BigInteger fileToBigInteger(File f, boolean signed)
    throws IOException
{
    byte[] array = new byte[file.length()];
    InputStream in = new FileInputStream(file);
    int i = 0; int r;
    while((r = in.read(array, i, array.length - i) > 0) {
        i = i + r;
    }
    in.close();
    if(signed) {
        return new BigInteger(array);
    }
    else {
        return new BigInteger(1, array);
    }
}

然后,您可以将BigInteger乘以并将结果保存在新文件中(使用toByteArray()方法)。

当然,这非常取决于您的文件格式 - 我的方法假设文件包含toByteArray()方法的结果,而不是其他格式。如果您有其他格式,请在问题中添加相关信息。

“我需要能够将文件内容乘以一个常数。”似乎是一个相当可疑的目标 - 您真正想要做什么?


0
一些测试表明,这将从文件中逐个返回[0…255]范围内的无符号字节值:
Reader bytestream = new BufferedReader(new InputStreamReader(
        new FileInputStream(inputFileName), "ISO-8859-1"));
int unsignedByte;
while((unsignedByte = bytestream.read()) != -1){
    // do work
}

看起来它适用于范围内的所有字节,包括 ISO 8859-1 中未定义字符的字节。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接