java - 仅使用 Java.IO 在文件中读取/写入字节

标签 java android arrays file java-io

我们如何在 Java 中将字节数组写入文件(并从该文件中读回)?
是的,我们都知道已经有很多这样的问题,但是由于有很多方法可以完成这项任务,因此它们变得非常困惑和主观。
所以让我们缩小问题的范围:
领域:

  • 安卓/Java

  • 我们想要什么:
  • 快(尽可能)
  • 无错误(以严格细致的方式)

  • 我们没有做什么:
  • 第三方库
  • 任何需要高于 23 (Marshmallow) 的 Android API 的库

  • (因此,排除了 Apache CommonsGoogle GuavaJava.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 - 这些方法永远不会“没有错误”。
  • 我认为在上述方法中不需要/不需要缓冲,因为只完成了一次 native 调用
    (另见 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/

    相关文章:

    java - 从 MySQL 数据库检索时间并用 Java 填充 JSpinner

    java - 如何用 Volley 替换 AsyncTask

    android - 设计可移动 View : Android

    php - 在 PHP 中将值数组转换为单个浮点值?

    arrays - 如果某些值不唯一而某些值唯一,如何在列表中排序和显示 json 值

    用于 ListView 的 JavaFX 平滑滚动

    java - HTMLUnit - 如何获取传出连接列表作为列表?

    java - 将焦点放在一个 JTextField 上并执行与其相关的操作

    Android 如何在其他 Activity 中停止 AlarmManager

    c - C 中的三角数组