【C++】C/C++字符串输入

1. C-风格字符串输入

1.1 scanf

使用 scanf 可以输入不带空格的字符串。

1
2
3
char str[100];
scanf("%s", str);
printf("%s\n", str);

输入: Hello World
输入: Hello

scanf 遇到空格后就会停止读入,余下的部分自动留在缓冲区内。

1
2
3
4
char str[100];
int i = 1;
while (~scanf("%s", str))
printf("String %d: %s\n", i++, str);

输入:
Hello World, Everyone!

输入上面一行内容,按下回车键后,会一次性输出三行内容。

输出:
String 1: Hello
String 2: World,
String 3: Everyone!

1.2 gets/fgets

gets

1
char* gets(char*buffer)

从键盘上输入字符,直至接受到换行符或 EOF 时停止,并将读取的结果存放在 buffer 指针所指向的字符数组中。读入完成后自动在 buffer 末尾添加 \0

注意,即使最后读取到了 \n 符号,也不会把它放入到 buffer 末尾

返回值为 buffer如果 EOF 了,返回 NULL

1
2
3
4
char str[100];
int i = 1;
while (gets(str))
printf("String %d: %s\n", i++, str);

输入输出:

1
2
3
4
Hello World! My friend.
String 1: Hello World! My friend.
Support Vector Machine
String 2: Support Vector Machine

注意到 str 末尾是没有换行符的,gets 把换行符丢弃了!

fgets

gets 在新的 C 标准中被废弃了,原因在于它是不安全的,可以用 fgets 替代。

1
char * fgets ( char * str, int num, FILE * stream );

需要在 num 参数处指定限制字符数量,然后在 stream 处指定输入流。

fgetsgets 有一个重要的不同。那就是它会保留输入中的换行符

1
2
3
4
5
6
const int MAX_LEN = 20;
char str[MAX_LEN];
int i = 1;
//注意,换行符会被保留
while (fgets(str, MAX_LEN, stdin))
printf("String %d: %s|\n", i++, str);

输入输出:

1
2
3
4
5
6
7
8
9
Hello World!
String 1: Hello World!
|
I am a boy!
String 2: I am a boy!
|
^Z

终端将被任务重用,按任意键关闭。

1.3 cin >>

1.1.1 使用方式

1
2
char sz[20];
cin >> sz;

1.1.2 细节知识点

cin 读取时,它会将回车键生成的换行符保留在缓冲区中。

这点很重要,与之后 getline()get() 函数出现的问题有关。

1.1.3 存在的问题

引例

1
2
3
4
5
6
char sz[20];
char str[20];
cin >> sz;
cin >> str;
cout << sz << endl;
cout << str << endl;

输入

1
hello world //按回车键

输出

1
2
hello
world

原因

cin 使用空白符号来确定字符串结束的位置,这意味着字符数组在输入时一次只读入一个单词。
cin 将该单词放入数组之中,自动添加空字符而输入的其余内容则留在缓冲区中

1.4 cin.getline()

1.2.1 使用方式

1
2
istream &getline(char *buffer, streamsize num);
istream &getline(char *buffer, streamsize num, char delim);

getline() 函数用于输入流,读取字符到 buffer 中,直到下列情况发生:

  • num - 1个字符已经读入,
  • 碰到一个换行标志,
  • 碰到一个EOF,
  • 任意地读入,直到读到字符delim。delim字符不会被放入buffer中。

样例

1
2
3
4
5
6
char sz[20];
char str[20];
cin.getline(sz, 20);
cin.getline(str, 20);
cout << sz;//注意没有endl
cout << str << endl;

输入

1
2
hello world //按回车键
here //按回车键

输出

1
2
hello worldhere
//endl空行

1.2.2 细节知识点

  1. 若参数num为n,则最多读入n-1个字符。
  2. getline() 函数每次读取一行。它通过换行符确定行尾,但不会将换行符留在缓冲区中,而是将换行符用空字符替换保留在字符串中。

以上面的例子为例,在输入hello world之后按下了回车键。

hello world //回车键

但输出时并没有空行。

1
2
hello worldhere//没有空行
空行

1.2.3 存在的问题

读入空行

引例

对一下程序

1
2
3
4
5
6
7
char sz[10];
char str[20];
cin >> sz;
cin.getline(str, 20);
cout << "sz: " << sz << endl;
cout << "str: " << str << endl;
return 0;

首先以一个输入方式为例。

  • 不会出错的输入
    1
    her name//按回车键

输出

1
2
her
name//注意前面的空格

cin在读到her后面的空格后结束,并且将空位符留在缓冲区中,由下一个字符串读取。

由此我们可以引出一个常见的错误

  • 会出错的输入
    1
    2
    her//按回车键
    //无法输入下一个字符串,直接输出

输出

1
her

这是由于cin将回车键产生的换行符留在缓冲区内所以直接被str读取。 所以str

注意,cin >>cin.getline()连用要小心

而对于cicin.getline()的连用,由于该函数不会将换行符留在缓冲区中,因此连用时没有读取空行的担忧。

也就是说

1
2
3
//这是可行的
cin.getline(sz, 20);
cin.getline(str, 20);

解决办法
1
2
3
4
5
cin >> sz;
//法一
cin.get();
//法二,ignore()默认忽略eof标识前一个字符
cin.ignore();

字数超长问题

如果输入行包含的字符比指定的字符数多,getline()会将剩余的字符留在缓冲区中,然后设置失效位,并关闭输入。

1.5 cin.get()

1.3.1 使用方式

原型

1
2
3
4
5
6
int get();
istream &get( char &ch );
istream &get( char *buffer, streamsize num );
istream &get( char *buffer, streamsize num, char delim );
istream &get( streambuf &buffer );
istream &get( streambuf &buffer, char delim );

get()函数被用于输入流,和以下这些:

  • 读入一个字符并返回它的值,
  • 读入一个字符并把它存储在ch,
  • 读取字符到buffer直到num - 1个字符被读入, 或者碰到EOF或换行标志,
  • 读取字符到buffer直到已读入num - 1 个字符,或者碰到EOF或delim(delim直到下一次不会被读取),
  • 读取字符到buffer中,直到碰到换行或EOF,
  • 读取字符到buffer中,直到碰到换行,EOF或delim。(相反, delim直到下一个get()不会被读取 ).

1.3.2 细节问题

2. string 输入

2.1 cin >>

2.1.2 使用方法

同上,一次只能读取一个单词。
注意这里读取时利用while的技巧

1
2
3
4
5
6
7
8
9
10
11
int main()
{
string line;
ifstream in;
//每次读取一个单词,直到文件末尾
while (in >> word)
{
cout << word << endl;
}
return 0;
}

2.2 getline()

2.2.1 原型

1
2
3
4
5
istream& getline (istream&  is, string& str, char delim);
istream& getline (istream&& is, string& str, char delim);

istream& getline (istream& is, string& str);
istream& getline (istream&& is, string& str);

2.2.2 使用方法

同上,遇到换行符为止(delim),但是终止字符delim(默认为换行符)并不会留在缓冲区中留给下一位来读取,而是变成了空字符留在了字符串中,不影响输出。(可以当作被丢弃了)

2.2.3 例子

有文件a.txt:

Iam a boy
Helloaworld

程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
ifstream fin("a.txt");
string str;
//每次读取一段文字,直到文件末尾EOF
while (getline(fin, str, 'a'))
{
cout << str << endl;
}
system("pause");
return 0;
}

输出结果:

1
2
3
4
5
I//遇到了a停止读入
m//字符a没有被读入下一个str
boy //遇到了a停止读入,但空格被下一个str读入了
Hello
world