C++逐行读取文件
Read a file line by line in C++
我写了下面的C++程序来逐行读取一个文本文件,并逐行打印出文件的内容。我在命令行中输入了文本文件的名称作为唯一的命令行参数。
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char* argv[])
{
char buf[255] = {};
if (argc != 2)
{
cout << "Invalid number of files." << endl;
return 1;
}
ifstream f(argv[1], ios::in | ios::binary);
if (!f)
{
cout << "Error: Cannot open file." << endl;
return 1;
}
while (!f.eof())
{
f.get(buf,255);
cout << buf << endl;
}
f.close();
return 0;
}
然而,当我运行这段代码在Visual Studio时,调试控制台是完全空白的。我的代码有什么问题?
除了评论中提到的错误外,该程序还有一个逻辑错误,因为 istream& istream::get(char* s, streamsize n)
没有按照您(或我,直到我调试它)认为的那样去做。是的,它读到下一个换行符;但是 it leaves the newline in the input!
下次您调用 get() 时,它会立即看到换行符并且 return 缓冲区中有一个空行,永远永远。
解决此问题的最佳方法是使用适当的函数,即 istream::getline()
,它 提取但不存储 换行符。
EOF 问题
值得一提。读取行的规范方法(如果你想写入字符缓冲区)是
while (f.getline(buf, bufSz))
{
cout << buf << "\n";
}
getline() returns 对流的引用,它又具有到 bool 的转换函数,这使得它可以在这样的布尔表达式中使用。如果可以获得输入,则转换为真。有趣的是,它可能遇到文件末尾,f.eof() 为真;但仅此一项并不能使流转换为 false
。只要它能提取至少一个字符,它就会转换为 true
,表明最后一个输入操作使输入可用,并且循环将按预期工作。
遇到EOF后的下一次读取会失败,因为无法提取数据:毕竟读取位置还在EOF。 那 被认为是读取失败。条件错误退出循环,正合本意
缓冲区大小问题
也值得一提。标准草案在 30.7.4.3 中说:
Characters are extracted and stored until one of the following occurs:
- end-of-file occurs on the input sequence (in which case the function calls setstate(eofbit));
- traits::eq(c, delim) for the next available input character c
(in which case the input character
is extracted but not stored);
- n is less than one or n - 1 characters are stored
(in which case the function calls setstate(
failbit)).
条件按此顺序测试,这意味着如果已存储 n-1 个字符并且下一个字符是换行符(默认分隔符),则输入是成功(换行符也被提取)。
这意味着,如果您的文件包含单行 123
,您可以使用 f.getline(buf, 4)
成功读取该行,但不能读取行 1234
(两者后面可能有也可能没有一个换行符)。
行尾问题
这里的另一个复杂情况是,在 Windows 上,使用典型编辑器创建的文件在换行符之前会有一个隐藏的回车符 return,即一行实际上看起来像“123\r\n” (“\r”和“\n”each 是单个字符,其值分别为 13 和 10)。因为您使用二进制标志打开文件,程序将看到回车 return;所有行都将包含该“不可见”字符,并且适合缓冲区的可见字符数将比假设的少一个。
控制台问题;-)
哦,您的控制台并不是完全空的;只是现代计算机速度太快,可能打印的第一行(在我的情况下)滚动离开的速度比任何人都可以切换 windows。当我仔细观察时,在左下角有一个光标,程序正忙着一行一行地打印空 ;-)。
结论
- 调试你的程序。使用 VS 非常简单。
- 使用
getline(istream, string)
.
- 使用输入函数的return值(通常是流)
作为 while 循环中的布尔值:“只要您可以提取任何输入,就使用该输入。”
- 注意行尾问题。
- 考虑 C I/O (printf, scanf) 任何重要的事情(我没有在我的回答中讨论这个,但我认为这是很多人所做的)。
我写了下面的C++程序来逐行读取一个文本文件,并逐行打印出文件的内容。我在命令行中输入了文本文件的名称作为唯一的命令行参数。
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char* argv[])
{
char buf[255] = {};
if (argc != 2)
{
cout << "Invalid number of files." << endl;
return 1;
}
ifstream f(argv[1], ios::in | ios::binary);
if (!f)
{
cout << "Error: Cannot open file." << endl;
return 1;
}
while (!f.eof())
{
f.get(buf,255);
cout << buf << endl;
}
f.close();
return 0;
}
然而,当我运行这段代码在Visual Studio时,调试控制台是完全空白的。我的代码有什么问题?
除了评论中提到的错误外,该程序还有一个逻辑错误,因为 istream& istream::get(char* s, streamsize n)
没有按照您(或我,直到我调试它)认为的那样去做。是的,它读到下一个换行符;但是 it leaves the newline in the input!
下次您调用 get() 时,它会立即看到换行符并且 return 缓冲区中有一个空行,永远永远。
解决此问题的最佳方法是使用适当的函数,即 istream::getline()
,它 提取但不存储 换行符。
EOF 问题
值得一提。读取行的规范方法(如果你想写入字符缓冲区)是
while (f.getline(buf, bufSz))
{
cout << buf << "\n";
}
getline() returns 对流的引用,它又具有到 bool 的转换函数,这使得它可以在这样的布尔表达式中使用。如果可以获得输入,则转换为真。有趣的是,它可能遇到文件末尾,f.eof() 为真;但仅此一项并不能使流转换为 false
。只要它能提取至少一个字符,它就会转换为 true
,表明最后一个输入操作使输入可用,并且循环将按预期工作。
遇到EOF后的下一次读取会失败,因为无法提取数据:毕竟读取位置还在EOF。 那 被认为是读取失败。条件错误退出循环,正合本意
缓冲区大小问题
也值得一提。标准草案在 30.7.4.3 中说:
Characters are extracted and stored until one of the following occurs:
- end-of-file occurs on the input sequence (in which case the function calls setstate(eofbit));
- traits::eq(c, delim) for the next available input character c (in which case the input character is extracted but not stored);
- n is less than one or n - 1 characters are stored (in which case the function calls setstate( failbit)).
条件按此顺序测试,这意味着如果已存储 n-1 个字符并且下一个字符是换行符(默认分隔符),则输入是成功(换行符也被提取)。
这意味着,如果您的文件包含单行 123
,您可以使用 f.getline(buf, 4)
成功读取该行,但不能读取行 1234
(两者后面可能有也可能没有一个换行符)。
行尾问题
这里的另一个复杂情况是,在 Windows 上,使用典型编辑器创建的文件在换行符之前会有一个隐藏的回车符 return,即一行实际上看起来像“123\r\n” (“\r”和“\n”each 是单个字符,其值分别为 13 和 10)。因为您使用二进制标志打开文件,程序将看到回车 return;所有行都将包含该“不可见”字符,并且适合缓冲区的可见字符数将比假设的少一个。
控制台问题;-)
哦,您的控制台并不是完全空的;只是现代计算机速度太快,可能打印的第一行(在我的情况下)滚动离开的速度比任何人都可以切换 windows。当我仔细观察时,在左下角有一个光标,程序正忙着一行一行地打印空 ;-)。
结论
- 调试你的程序。使用 VS 非常简单。
- 使用
getline(istream, string)
. - 使用输入函数的return值(通常是流) 作为 while 循环中的布尔值:“只要您可以提取任何输入,就使用该输入。”
- 注意行尾问题。
- 考虑 C I/O (printf, scanf) 任何重要的事情(我没有在我的回答中讨论这个,但我认为这是很多人所做的)。