LintCode 660: Read N Characters Given Read4 II - Call multiple times (嵌入式设计经典题!)

  1. Read N Characters Given Read4 II - Call multiple times

The API: int read4(char *buf) reads 4 characters at a time from a file.

The return value is the actual number of characters read. For example, it returns 3 if there is only 3 characters left in the file.

By using the read4 API, implement the function int read(char *buf, int n) that reads n characters from the file.

Example
Example 1

Input:
“filetestbuffer”
read(6)
read(5)
read(4)
read(3)
read(2)
read(1)
read(10)
Output:
6, buf = “filete”
5, buf = “stbuf”
3, buf = “fer”
0, buf = “”
0, buf = “”
0, buf = “”
0, buf = “”
Example 2

Input:
“abcdef”
read(1)
read(5)
Output:
1, buf = “a”
5, buf = “bcdef”
Notice
The read function may be called multiple times.

解法1:
注意

  1. 这题要求多次call,所以我们应该单独设一个char buff[4],这样如果buff里面有4个char的话,这次读了一个char,下次还可以继续从buff里面读,就不用再调用read4这个系统函数了。buff应该是类里面的全局变量,这样下次调用还可以接着用buff里面的数据。
  2. 我们还要设一个readPos和WritePos作为类里面的全局变量。readPos表示这次buff里面已经读到的位置。writePos表示调用read4()之后在buff里面写到什么位置了。同样因为read()会多次调用,它们应该放在函数外面作为全局变量。
  3. 什么时候需要调用read4()呢? 可以把这道题当成一个producer-consumer模型。其中buff就是缓冲区。当readPos==writePos的时候,说明buff中的未读数据已经读完了,如果i<n的话,要再call一次read4()。
  4. 这里因为read4()不支持循环buffer (ring buffer),所以每次调用read4(),writePos都是0+read4()实际读的char数。而因为read4()不支持循环buffer,每次调用read4(),readPos也就必须设为0。
  5. 如果writePos==0,说明这次read4根本就没有读到什么新数据,也就是说文件已经读到头了,实际上上次read4()就已经读完了,那么就可以直接退出,返回i即可,i就是实际读到的总数据。
  6. 如果0<writePos<4,那么程序还会在for循环里多执行几次buf[i] = buff[readPos++],直到readPos==writePos,然后退出。
  7. 如果题目说的是只调用一次read(),那么就不需要再开一个buff了。只需要一个cur(类似于writePos)。程序可以写成如下:
    int read4(char *buf);
    class Solution {
    public:
    int read(char *buf, int n) {
    int res = 0;
    for (int i = 0; i <= n / 4; ++i) {
    int cur = read4(buf + res);
    if (cur == 0) break;
    res += cur;
    }
    return min(res, n);
    }
    };
// Forward declaration of the read4 API.
int read4(char *buf);

class Solution {
public:
    /**
     * @param buf destination buffer
     * @param n maximum number of characters to read
     * @return the number of characters read
     */
     
    int read(char *buf, int n) {
        for (int i = 0; i < n; ++i) {
            if (readPos == writePos) {
                writePos = read4(buff);
                readPos = 0;
                if (writePos == 0) return i;   //reach the end of the file
            }
            
            buf[i] = buff[readPos++];
        }

        return n;    
    }
private:
    char buff[4];   
    int writePos = 0;
    int readPos = 0;
};

解法2:
思路跟解法1类似。

// Forward declaration of the read4 API.
int read4(char *buf);

class Solution {
public:
    /**
     * @param buf destination buffer
     * @param n maximum number of characters to read
     * @return the number of characters read
     */
     
    int read(char *buf, int n) {
        int readCount = 0;
        while(readCount < n) {
            if (readPos == 0) {
                bytesRead = read4(buff);
            }
            
            while(readCount < n && readPos < bytesRead) {
                buf[readCount++] = buff[readPos++];
            }
            
            if (readPos == bytesRead) readPos = 0;

            if (bytesRead < 4) break; // no need to continue to call read4() again
        }
        

        return readCount;    
    }

private:
    char buff[4];   
    int bytesRead = 0;
    int readPos = 0;
};

代码同步在
https://github.com/luqian2017/Algorithm

三刷:
注意

  1. read4()返回的是实际读的个数,只要文件没到末尾,它就会读4个字节到tmpBuff,如果文件到了末尾,它就把读到文件末尾为止的内容放到tmpBuff。
  2. read(buf, n)只会读n个字节到buf,也就是说,tmpBuff里面可能还会有几个字节没读完,这样,下次的read()会先从tmpBuff里面读,然后当read_pos==read_count了才会重新调用read4()。
    比如说文件内容是"filetestbuffer",那么
    read(6): 会call read4()读4个字节到tmpBuff,然后这4个字节会读到buf,然后再call read4()读4个字节到tmpBuff,这时只读2个字节到buf,因为总共只需6个字节。此时tmpBuff里面剩2个字节。
    read(5): 会先读tmpBuff里面剩下的2个字节到buf,然后再call read4()读4个字节到tmpBuff,这时只读3个字节到buf,因为总共只需5个字节。此时tmpBuff里面剩1个字节。
    read(4): 会先读tmpBuff里面剩下的1个字节到buf, 然后再call read4(),这时候由于文件已经到末尾,所以只读2个字节到tmpBuff,这2个字节会被读到buf。此时tmpBuff为空。
    read(3): 此时文件已经读到末尾,tmpBuf为空,所以read4()会返回0,也不会读数据到buf。
// Forward declaration of the read4 API.
int read4(char *buf);
char tmpBuff[4];
int read_count = 0, read_pos = 0;
class Solution {
public:
    /**
     * @param buf destination buffer
     * @param n maximum number of characters to read
     * @return the number of characters read
     */
    int read(char *buf, int n) {
        cout << " read(), n = " << n << endl;
        int res = 0;
        for (int i = 0; i < n; i++) {
            if (read_pos == read_count) {
                read_count = read4(tmpBuff);
                cout << " i = " << i << " read_count = " << read_count << endl;
                read_pos = 0;
            }
            if (read_pos < read_count) {
                buf[i] = tmpBuff[read_pos++];
                res++;
            }
        }        

        return res;
    }
};

Input Data
“filetestbuffer”
read(6)
read(5)
read(4)
read(3)
read(2)
read(1)
read(10)

StdOut
read(), n = 6
i = 0 read_count = 4 read_pos = 0
i = 4 read_count = 4 read_pos = 4
read(), n = 5
i = 2 read_count = 4 read_pos = 4
read(), n = 4
i = 1 read_count = 2 read_pos = 4
i = 3 read_count = 0 read_pos = 2
read(), n = 3
i = 0 read_count = 0 read_pos = 0
i = 1 read_count = 0 read_pos = 0
i = 2 read_count = 0 read_pos = 0
read(), n = 2
i = 0 read_count = 0 read_pos = 0
i = 1 read_count = 0 read_pos = 0
read(), n = 1
i = 0 read_count = 0 read_pos = 0
read(), n = 10
i = 0 read_count = 0 read_pos = 0
i = 1 read_count = 0 read_pos = 0
i = 2 read_count = 0 read_pos = 0
i = 3 read_count = 0 read_pos = 0
i = 4 read_count = 0 read_pos = 0
i = 5 read_count = 0 read_pos = 0
i = 6 read_count = 0 read_pos = 0
i = 7 read_count = 0 read_pos = 0
i = 8 read_count = 0 read_pos = 0
i = 9 read_count = 0 read_pos = 0

Output Data
6, buf = “filete”
5, buf = “stbuf”
3, buf = “fer”
0, buf = “”
0, buf = “”
0, buf = “”
0, buf = “”

Expected
6, buf = “filete”
5, buf = “stbuf”
3, buf = “fer”
0, buf = “”
0, buf = “”
0, buf = “”
0, buf = “”

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值