- 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:
注意
- 这题要求多次call,所以我们应该单独设一个char buff[4],这样如果buff里面有4个char的话,这次读了一个char,下次还可以继续从buff里面读,就不用再调用read4这个系统函数了。buff应该是类里面的全局变量,这样下次调用还可以接着用buff里面的数据。
- 我们还要设一个readPos和WritePos作为类里面的全局变量。readPos表示这次buff里面已经读到的位置。writePos表示调用read4()之后在buff里面写到什么位置了。同样因为read()会多次调用,它们应该放在函数外面作为全局变量。
- 什么时候需要调用read4()呢? 可以把这道题当成一个producer-consumer模型。其中buff就是缓冲区。当readPos==writePos的时候,说明buff中的未读数据已经读完了,如果i<n的话,要再call一次read4()。
- 这里因为read4()不支持循环buffer (ring buffer),所以每次调用read4(),writePos都是0+read4()实际读的char数。而因为read4()不支持循环buffer,每次调用read4(),readPos也就必须设为0。
- 如果writePos==0,说明这次read4根本就没有读到什么新数据,也就是说文件已经读到头了,实际上上次read4()就已经读完了,那么就可以直接退出,返回i即可,i就是实际读到的总数据。
- 如果0<writePos<4,那么程序还会在for循环里多执行几次buf[i] = buff[readPos++],直到readPos==writePos,然后退出。
- 如果题目说的是只调用一次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
三刷:
注意
- read4()返回的是实际读的个数,只要文件没到末尾,它就会读4个字节到tmpBuff,如果文件到了末尾,它就把读到文件末尾为止的内容放到tmpBuff。
- 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 = “”