我们如何在 Java 中将字节数组写入文件(并从该文件中读回)?
是的,我们都知道已经有很多这样的问题,但是由于有很多方法可以完成这项任务,因此它们变得非常困惑和主观。
所以让我们缩小问题的范围:
领域:
我们想要什么:
我们没有做什么:
(因此,排除了 Apache Commons , Google Guava , Java.nio ,并留下了 good ol' Java.io )
我们需要的:
在我的特定情况下,这些方法是私有(private)的(不是库)并且不负责以下内容,(但如果您想创建一个适用于更广泛受众的更通用的解决方案,那就去做吧):
所以......我们正在寻找 future 人们可以认为可以安全使用的最终防弹代码,因为您的答案有很多赞成票,并且没有评论说,“这可能会崩溃如果...”
这是我到目前为止所拥有的:
将字节写入文件:
private void writeBytesToFile(final File file, final byte[] data) {
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(data);
fos.close();
} catch (Exception e) {
Log.i("XXX", "BUG: " + e);
}
}
从文件中读取字节:private byte[] readBytesFromFile(final File file) {
RandomAccessFile raf;
byte[] bytesToReturn = new byte[(int) file.length()];
try {
raf = new RandomAccessFile(file, "r");
raf.readFully(bytesToReturn);
} catch (Exception e) {
Log.i("XXX", "BUG: " + e);
}
return bytesToReturn;
}
根据我的阅读,可能的异常(exception)是:FileNotFoundException:我是否正确,只要提供的文件路径是使用 Android 自己的内部工具派生的和/或如果应用程序经过正确测试,这种情况就不应该发生?
IOException :我真的不知道是什么导致了这种情况......但我假设如果它确实没有办法解决它。
所以考虑到这一点......这些方法可以改进或替换,如果可以,用什么?
最佳答案
看起来这些将成为必须在 Android API 23 或更高版本上运行的核心实用程序/库方法。
关于库方法,我发现最好不要假设应用程序将如何使用这些方法。在某些情况下,应用程序可能希望接收检查 IOException
s(因为文件中的数据必须存在才能使应用程序工作),在其他情况下,应用程序甚至可能不关心数据是否不可用(因为文件中的数据只是缓存,也可以从主要来源获得)。
当涉及到 I/O 操作时,永远无法保证操作会成功(例如,用户将手机掉在厕所里)。库应该反射(reflect)这一点,并让应用程序选择如何处理错误。
为了优化 I/O 性能,始终假设“快乐路径”并捕获错误以找出问题所在。这与普通编程相反,但在处理存储 I/O 时必不可少。例如,在读取文件之前仅检查文件是否存在会使您的应用程序变慢两倍 - 所有这些类型的 I/O 操作加起来很快就会减慢您的应用程序的速度。假设文件存在,如果出现错误,则仅检查文件是否存在。
因此,鉴于这些想法,主要功能可能如下所示:
public static void writeFile(File f, byte[] data) throws FileNotFoundException, IOException {
try (FileOutputStream out = new FileOutputStream(f)) {
out.write(data);
}
}
public static int readFile(File f, byte[] data) throws FileNotFoundException, IOException {
try (FileInputStream in = new FileInputStream(f)) {
return in.read(data);
}
}
实现注意事项:NullPointerException
s - 这些方法永远不会“没有错误”。 (另见 here)。
为了使应用程序更容易读取文件,可以添加一个附加方法。但请注意,由库来检测任何错误并将其报告给应用程序,因为应用程序本身无法再检测到这些错误。
public static byte[] readFile(File f) throws FileNotFoundException, IOException {
int fsize = verifyFileSize(f);
byte[] data = new byte[fsize];
int read = readFile(f, data);
verifyAllDataRead(f, data, read);
return data;
}
private static int verifyFileSize(File f) throws IOException {
long fsize = f.length();
if (fsize > Integer.MAX_VALUE) {
throw new IOException("File size (" + fsize + " bytes) for " + f.getName() + " too large.");
}
return (int) fsize;
}
public static void verifyAllDataRead(File f, byte[] data, int read) throws IOException {
if (read != data.length) {
throw new IOException("Expected to read " + data.length
+ " bytes from file " + f.getName() + " but got only " + read + " bytes from file.");
}
}
这个实现增加了另一个隐藏的失败点:OutOfMemory 在创建新数据数组的点。为了进一步适应应用程序,可以添加其他方法来帮助处理不同的场景。例如,假设应用程序真的不想处理已检查的异常:
public static void writeFileData(File f, byte[] data) {
try {
writeFile(f, data);
} catch (Exception e) {
fileExceptionToRuntime(e);
}
}
public static byte[] readFileData(File f) {
try {
return readFile(f);
} catch (Exception e) {
fileExceptionToRuntime(e);
}
return null;
}
public static int readFileData(File f, byte[] data) {
try {
return readFile(f, data);
} catch (Exception e) {
fileExceptionToRuntime(e);
}
return -1;
}
private static void fileExceptionToRuntime(Exception e) {
if (e instanceof RuntimeException) { // e.g. NullPointerException
throw (RuntimeException)e;
}
RuntimeException re = new RuntimeException(e.toString());
re.setStackTrace(e.getStackTrace());
throw re;
}
方法fileExceptionToRuntime
是一个最小的实现,但它在这里展示了这个想法。该库还可以帮助应用程序在发生错误时进行故障排除。例如,方法
canReadFile(File f)
可以检查一个文件是否存在并且是可读的并且不是太大。应用程序可以在文件读取失败后调用此类函数并检查无法读取文件的常见原因。写入文件也可以这样做。
关于java - 仅使用 Java.IO 在文件中读取/写入字节,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70074875/