C/C++查缺补漏:I/O处理、函数对象介绍

前言

  • 为了避免大量内容挤在一篇文章里,这里将关于泛型算法和I/O的讨论放在这一章
  • 本章I/O处理部分包括了在刷算法题、leetcode、uva中常遇到包括但不限于下列问题:
    1. 输入处理、输出处理
    2. 字符、数字转换
    3. 字符串、数组转换
    4. 字符串分割
  • 做一些对输入处理要求较高的算法题时,常常会发现这类题的解决方案很简单,算法也很容易,但是唯独在处理输入上花费大量时间。本文I/O部分除了介绍基本的c++标准库中的I/O外,还附加介绍如何在这类题目中利用I/O处理这类问题。

函数对象

  • 可以像调用函数一样调用的对象。
  • 函数对象常用于泛型算法的实参。
  • 函数对象的本质就是重载函数调用运算符,即括号
class Test
{
public:
    void operator() (int a)//重载函数调用运算符
    {
        cout << a << endl;
    }
};

int main(int argc, char const *argv[])
{
    Test t;
    t(3);
    
    return 0;
}

I/O处理

  • I/O处理的内容非常广泛,除了和标准输入输出相关的I/O,文件I/O和字符串I/O都很常用,具有大量实用功能。本文尚未介绍文件I/O,将在后续进行补充
  • I/O library中有三组常用的I/O classes,分别是
    在这里插入图片描述
  • 继承关系满足

derived class -> base class

在这里插入图片描述

条件状态位

  • 这些状态位对所有I/O library的I/O classes都支持

在这里插入图片描述

  • failbit:例如,当往一个int类型变量输入一个char时,failbit会置1。一般failbit是指示发生了可恢复的错误
  • badbit:发生了系统级的读写错误时置位,一般不可恢复
  • while(cin >> a) == while(cin.good())
  • 置位:cin.setstate(s.badbit & ~s.good() & s.failbit & ~s.endbit)
  • 获取当前状态位:auto state = cin.rdstate()

sstring

字符串输入:getline()

  • input:输入流
  • str:用于存储从输入流中获取的字符串
  • delim:分割符号。当str在输入流input中遇到该符号时停止读入,并舍弃该符号
  • 更详细的函数原型相关请自行查阅官方文档
    在这里插入图片描述
  • I/O处理上,继承机制中很可能采用了虚函数+base class的引用或指针+动态绑定从而实现多态的方式。这就意味着在函数原型中的std::basic_istream<..>& input(即istream)引用类型参数可以是ifstreamistringstream
string s;
getline(cin, s);

cout << s;
  • 尝试使用ifstreamistringstream作为参数:
//istringstream
string s("1234");
istringstream is(s);
string t;

getline(is, t);

cout << t;
//输出:1234

//ifstream
ifstream f("Output.c");
string t;

getline(f, t);

cout << t;
//输出:文件中的第一行,除了换行符以外其它都包括
  • 很好!我们发现getline可以拓展到字符串流和文件流中操作,这样为我们读取一行提供给了极大的方便。
  • 由于getline在istringstream上的强大操作,它还可以帮助我们实现许多功能。

字符串分割

  • 假设现在从stdin或者文件中读入一行单词,每个单词之间如果是用空格分割的,那么提取单词的方式其实很简单:
ifstream f("Output.c");
string s;

while(f >> s)
    cout << s << endl;

  • 但如果单词之间的分割是某个符号呢?比方说逗号或者分号,上面的办法就无效了,因为逗号和分号也会被当作char类型。我们可以利用getline:
ifstream f("Output.c");
string s;

while(getline(f, s, ','))
    cout << s << endl;

获取字符串和流的内容

string s("1234");
istringstream is(s)
cout << s.c_str() << endl;
//returns a non-modifiable standard C character array 
//version of the string
cout << is.str() << endl;

字符转数字

char a = '3';
cout << (int)a-48 << endl;
cout << static_cast<int>(a)-48 << endl;
//48这个常数不变

数字转字符

int a = 9;
cout << (char)(a+48) << endl;
//注意括号

字符串转数组

string s("1234");
istringstream is(s);
int a[4];
char c;

for(int i = 0; i < 4; ++i)
{
	is >> c;
	a[i] = (int)c-48;
}

字符串转数字

string s("1234");
istringstream is(s);
int a;

is >> a;
//注意!
//istringstream会把流内的内容“给”外部变量且不自我保留
//故在下一次使用istringstream向外部输出时,会发现stream内已空
//但用于给stream初始化的string不会丢失,因为流内的内容是从
//string 复制进去的

数组转字符串

int a[4] = {1, 2, 3, 4};
string s;
for(int i = 0; i < 4; ++i)
	s.push_back((char)(a[i]+48));

数字转字符串

int a = 1234;

string s;
ostringstream os;

os << a;

s.assign(os.str());
//注意!
//用ostringstream os(s)的方式将os和s绑定
//使用 os << a的方式
//不会给s带来任何改变
//os(s)这种构造方式只会给os的 underlying string device object 一个初始值
//事实上大多时候我们也不需要os(s)这样的操作

Tips

从上面的实验可以发现一些结论

string用string s的方式来定义,没有给初始值,这么做string底层存储字符串的空间是否为空?直接给这样的s赋值是否会导致内存溢出?

  • Ans:这么做是没有任何问题的。通过调用s.capacity()可以发现,s在刚开始定义时就已经分配了空间。通过s.reserve(n),其中n为期望s分配的新的空间大小。
  • 当n < s.capacity()时该调整无效;当n > s.capacity()时调整有效

如何清空stringstream

  • 注意:每次使用同一个stringstream对象前都要进行标志位和内存的清空,否则下一次往stringstream对象内输入字符串不会改变其内容,甚至有可能会报错
  • 清空操作:
stringstream ss;
ss.clear();//清空状态标志位
ss.str("");//清空内容,双引号内没有字符
  • 两种清空缺一不可!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值