题目描述

✅ 157. 用 Read4 读取 N 个字符

题意分析

已有接口 read4,每次从文件当前位置读取最多 4 个字符,写入传入的缓冲区,并返回实际读到的数量。需要用它实现一次最多向目标缓冲区写入 n 个字符的 read,返回实际写入量。

文件可能不足 n 个字符,也可能比需求更长。要同时遵守文件的实际长度与本次请求上限,不能把底层读取量直接当作交付量。

解法:分批 read4 拷贝

核心思路

[!blue]

准备长度为 4 的临时缓冲 tmp,让 read4 总是先读入这里。用 copied 表示已经写入目标缓冲的字符数,它同时也是下一次复制的目标起点。

本轮 read4 返回 cnt 后,只有 tmp 的前 cnt 个字符有效,而本次请求还需要 n - copied 个字符。因此实际复制量应为 take = min(cnt, n - copied),复制完成后只把 take 加入 copied。

当 cnt < 4 时,说明底层已读到文件末尾,但这批仍可能包含有效字符,所以必须先复制,再结束。若恰好读到 4 个,单凭这个数量无法判断文件是否还有内容;只要需求未满足,就继续调用,下一次返回 0 也能正常结束。

本题的 read 只调用一次,最后一批超过请求数量的字符不需要留给后续调用。达到 copied == n 时直接停止,不再进行无用的底层读取。

解题步骤

  1. 创建四字符临时缓冲,将已交付数量 copied 设为 0。
  2. 当 copied < n 时,调用 read4(tmp),得到本批有效字符数 cnt。
  3. 计算 take = min(cnt, n - copied),把这部分字符复制到目标缓冲的 copied 位置,并增加已交付数量。
  4. 若 cnt < 4,文件已结束,跳出循环;否则继续由剩余需求决定是否读取下一批。
  5. 返回 copied,而不是请求的 n 或所有批次的底层读取量之和。

代码实现

public class Solution extends Reader4 {
    public int read(char[] buf, int n) {
        char[] tmp = new char[4];
        int copied = 0;

        while (copied < n) {
            int cnt = read4(tmp);
            // 本批最多交付有效字符数与剩余需求中的较小值。
            int take = Math.min(cnt, n - copied);

            System.arraycopy(tmp, 0, buf, copied, take);
            copied += take;

            // 先复制本批有效字符,再根据短读取判断文件结束。
            if (cnt < 4) {
                break;
            }
        }

        return copied;
    }
}
func read(buf []byte, n int) int {
    tmp := make([]byte, 4)
    copied := 0

    for copied < n {
        cnt := read4(tmp)
        // 本批最多交付有效字符数与剩余需求中的较小值。
        take := cnt
        if n-copied < take {
            take = n - copied
        }
        copy(buf[copied:], tmp[:take])
        copied += take
        // 先复制本批有效字符,再根据短读取判断文件结束。
        if cnt < 4 {
            break
        }
    }

    return copied
}

复杂度分析

  • 时间复杂度:上界为 $O(n+1)$,最多交付 n 个字符,文件提前结束时更早停止。
  • 空间复杂度:$O(1)$,固定四字符缓冲,目标输出不计入辅助空间。

关键点总结

[!green]

  • 临时缓冲接收底层批次,目标缓冲只接收用户本次需要的部分。
  • cnt 表示本批读到多少,take 表示本批交付多少,copied 表示累计交付多少。
  • 结束条件有两个:请求已满足,或最后一批有效字符已处理完且文件到尾。

易错点总结

[!yellow]

  • 读到不足四个字符时不能立即退出,必须先复制这批有效内容。
  • 不能总是复制四个字符,也不能总是复制 cnt 个;最后一批还可能受剩余请求数量限制。
  • copied 只能增加实际复制量,按 cnt 增加可能使返回值超过 n。
  • 多次调用版本需要保留尚未交付的字符,本题单次调用的局部缓冲不能直接代替那种缓存设计。

相似题目

题目 难度 关联与区别
158. 用 Read4 读取 N 个字符 II - 多次调用 困难 原题read可多次调用,多读出的字符必须跨调用缓存;本题只调用一次。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2024/28035341
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!