c++输入输出流

输入输出流类关系图

iostream头文件中4种流对象

对象 含义 对应设备 对应的类 C语言对应文件
cin 标准输入流 键盘 istream_withassign stdin
cout 标准输出流 控制台 ostream_withassign stdout
cerr 标准错误流 控制台 ostream_withassign stderr
clog 标准错误流 控制台 ostream_withassign stderr

  1. cout流在内存中对应开辟了一个缓存区,用来存放流中的数据,当向cout流插入一个endl时,不论缓冲区是否已满,都立即输出流中的所有数据,然后插入一个换行符,并刷新流(清空缓冲区), 注意如果插人一个换行符”\n“(如cout<<a<<"\n"),则只输出和换行,而不刷新cout 流(但并不是所有编译系统都体现出这一区别)

  2.cout 流通常是传送到显示器输出,但也可以被重定向 输出到磁盘文件,而cerr流中的信息只能在显示器输出,cerr是不经过缓冲区,直接向显示器上输出有关信息,而clog中的信息存放在缓冲区中,缓冲区满后或遇endl时向显示器输出

1. 标准输入流cin

  (1)常用函数

cin.get() 返回单个字符
cin.get(char cval) 返回单个字符
cin.get(_Elem *_Str, streamsize _Count) 返回Count字符的字符串
cin.get(Elem *_Str,count, _Elem _Delim) Delim为结尾符
cin.getline(char *_Str, Count, char _Delim)  
cin.ignore() 读取字符并忽略指定字符
cin.peek() 检查下一个输入的字符,不会把字符从流中移除
cin.putback(char cVal)  返回一个字符给一个流
1. 使用cin从流中读出的字符,流中就没有字符了,再次读取时只能读取剩下的
2. 缓冲区只有在遇到EOF、手动敲回车,流缓存区满时,才能将流中的字符全部读出(即清空缓存区)

  (2)示例

#include "stdio.h"
#include <iostream>
using namespace std;

void main()
{
    // 从流中读取个字符然后再放进去
    char cVal = cin.get();
    cin.putback(cVal);
    
    // 判断流中的第一个字符是不是刚放进去的字符
    if (cin.peek() == cVal)
    {
        cout << "流中的第一个字符是刚放进去的字符" << endl;
    }
    else
    {
        cout << "流中的第一个字符不是刚放进去的字符" << endl;
    }

    // 从流中读取10个字符
    char cBuff[11] = {0};
    cin.get(cBuff,11);  // 注意这里是11

    // 从流中忽略5个字符,再读取10个字符
    cin.ignore(5);
    char cBuff1[11] = {0};
    cin.get(cBuff1, 11, EOF);

    // 读取剩下的字符
    char cBuff2[100] = {0};
    cin.get(cBuff2, 100);

    //输出读到的数据
    cout<<"第一个字符"<<cVal<<endl;
    cout<<"第一组 字符串:"<<cBuff<<endl;
    cout<<"第二组 字符串:"<<cBuff1<<endl;
    cout<<"剩下的字符串:"<<cBuff2<<endl;

    system("pause");

    return;
}

2. 标准输出流cout

  (1)用于控制输出格式的流成员函数

流成员函数 作用相同的控制符 作用
precision(n) setprecision(n) 设置实数的精度
width(n) setw(n) 设置字符宽度
fill(char cVal) setfill(char cVal) 设置填充字符
setf() setiosflags() 设置输出各格式状态
unsetf() resetiosflags() 终止已经设置的输出格式状态,在括号中应指定内容

  (2)设置格式状态的格式标志

格式标志 作用
ios::left 输出数据在本域宽范围内向左对齐
ios::right 输出数据在本域宽范围内向右对齐
ios::internal 数值的符号位在域宽内左对齐,数值右对齐,中间由填充字符填充
ios::dec 设置整数的基数为10
ios:oct 设置整数的基数为8
ios::hex 设置整数的基数为16
ios::showbase 强制输出整数的基数(八进制以0开头,十六进制以0x开头)
ios::showpoint 强制输出浮点数的小树和尾数0
ios::uppercase 以科学计数法格式E和以十六进制输出字母时以大写表示
ios::showpos 对正数显示‘+’号
ios::scientific 浮点数以科学计数法格式输出
ios::fixed 浮点数以定点格式(小数形式)输出
ios::unitbuf 每次输出之后刷新所有的流
ios::stdio 每次输出之后清除stdout/stderr
eg : cout.setf( ios::dec );

       cout.setf(ios::hex,ios::basefield);【建议使用这种】

需注意:
1、fmtflags setf( fmtflags flags ); 使用这种,一定要先取消当前基【cout.unself()】,之后才可以设置新的基

 2、fmtflags setf( fmtflags flags, fmtflags needed ); 使用这种,第二个参数设为当前的基,或者当不知道当前基时,设为ios_base::basefield清除当前的所有可能的基

  (3)输入输出流的控制符

控制符 作用
dec 设置基数10
oct 设置基数8
hex 设置基数16
setfill(c) 用字符填充
setprecision(n) 设置精度
setw(n) 设置宽度
setiosflags(ios::fixed) 设置浮点数以固定的小数位数显示
setiosflags(ios::scientific) 设置浮点数以科学计数法显示
setiosflags(ios::skipws) 忽略前导的空格
setiosflags(ios::left) 左对齐
setiosflags(ios::right) 右对齐
setiosflags(ios::uppercase) 数据以16进制输出时字母大写
setiosflags(ios::lowercase) 数据以16进制输出时字母小写
setiosflags(ios::showpos) 输出正数时给出+号
如果使用了控制符,需要加头文件include<iomanip>

文件输入输出流fstream

  ofstream, ifstream, fstream

  在头文件fstream中,在fstream类中,用open成员函数打开文件,实现类与文件的关联操作。

  - open(filename, mode, prot):对所属类进行用指定的参数进行特定的文件关联

              几种打开方式(mode)(这些方式可以用 '|' 组合起来)

1. 文件打开方式

ios::in 读文件
ios::out 写文件
ios::app 所有写入附加在文件末尾
ios::ate 打开一个已有文件,文件指针指向文件末尾
ios::trunc 打开文件,若文件存在则删除全部数据,若文件不存在则建立新文件,如已指定ios::out方式,而未指定ios::app,ios::ate,ios::in,则默认此方法
ios::binary 二进制打开文件

2. 属性值设置

  打开文件的属性,这些方式可以用‘+’进行组合,这个参数可以省略不传

0 普通文件,打开操作
1 只读文件
2 隐含文件
4 系统文件

3. 文件状态判断

is_open() 文件是否正常打开
bad() 读写过程中是否出错
fail() 读写过程中是否出错
eof() 读文件到达文件末尾,返回True
good() 以上任何一个返回True,这个就返回False

4. 流指针设置

  - 获得和设置流指针

    - 对于所有的输入输出流都有至少一个指针,指向下一个要操作的位置

      ofstream  put_point

      ifstream  get_point

      fstream  put_point和get_point

    - 获取流指针位置

      tellg(): 返回输入流指针的位置(返回类型long)

      tellp(): 返回输出流指针的位置(返回类型long)

    - 设置指针位置

      seekg(long  position):  设置输入流指针位置为第position个字符(文件首位置为开始位置)

      seekp(long  position):  设置输出流指针到指定位置

istream &seekg(streamoff offset,seek_dir origin);
ostream &seekp(streamoff offset,seek_dir origin);
ios::beg:  文件开头
ios::cur:  文件当前位置
ios::end:  文件结尾

字符串输入输出流sstringstream

  ostringstream:用于执行C风格字符串的输出操作

  istringstream:用于执行C风格字符串的输入操作

  stringstream:同时支持C风格字符串的输入输出操作

  例1:

#include "stdio.h"
#include <iostream>
#include <sstream>
using namespace std;

void ostringstream_test()
{
    ostringstream oss;
    oss << "this is test" << '\t'<< 123456;
    cout << oss.str() << endl;
    oss.str("");  // 清空之前的内容
    // oss.clear(); // 并不能清空内存
    cout << oss.str() << endl;

    // 浮点数转换限制
    double dbVal = 123.123455677;
    oss << dbVal;
    cout << oss.str() << endl;
    oss.str("");
    oss.precision(9);
    oss.setf(ios::fixed, ios::basefield);  // 将浮点数的位数限定为小数点之后的位数
    oss << dbVal;
    cout << oss.str() << endl;
}

  例2:

void istringstream_test()
{
    // 输入
    stringstream ss;
    ss << "this is test" << ' '<< 123456;
    cout << ss.str() << endl;

    // 输出
    string out;
    while (ss >> out)
    {
        cout << out.c_str() << endl;
    }
}

例:日志信息输出

(1)功能思维导图

  

(2)代码实现

Logging.h
#pragma once
#include <stdio.h>
#include <Windows.h>

#define MAX_BUFFSIZE 8192
namespace Logger
{
    enum LOGMODE
    {
        CONSOLE = 0x1,
        LOGFILE = 0x2,
        CONSOLE_FILE=0x3
    };

    enum LOGLEVEL
    {
        INFO = 0,
        DEBUG
    };

    class CLogging
    {
    public:
        explicit CLogging();
        explicit CLogging(const int LogMode, char *pFileName=NULL, char *pFileMode=NULL);
        ~CLogging(void);
        void LogPrintf(char* pFormat, ...);

    private:
        inline void OutputLoginfo(const char *pBuff);

    private:
        TCHAR *m_pLogFileName;
        TCHAR *m_pLogFileMode;
        int m_LogMode;
        FILE *m_pFile;
        HANDLE m_hMuex;
    };
}
Logging.cpp
#include "Logging.h"
#include <time.h>

namespace Logger
{
    CLogging::CLogging(void)
    {
        m_pLogFileName = NULL;
        m_pLogFileMode = NULL;
        m_pFile = NULL;
        m_hMuex = NULL;
    }

    CLogging::CLogging( const int LogMode, char *pFileName, char *pFileMode ):
        m_LogMode(LogMode),m_pLogFileName(pFileName),m_pLogFileMode(pFileMode)
    {
        m_hMuex = CreateMutex(NULL, 0, NULL);
        m_pFile = NULL;

        switch (LogMode)
        {
        case CONSOLE:
            break;
        case LOGFILE:
        case CONSOLE_FILE:
            {
                m_pFile = fopen(m_pLogFileName, pFileMode);
                if (!m_pFile)
                {
                    throw ("Open LogFile Error!");
                }
            }
            break;
        }
    }

    CLogging::~CLogging(void)
    {
        if (m_pFile)
        {
            fclose(m_pFile);
        }
        if (m_hMuex!=NULL)
        {
            CloseHandle(m_hMuex);
        }
    }

    void CLogging::LogPrintf(char* pFormat, ... )
    {   
        DWORD nRet = WaitForSingleObject(m_hMuex, 5000);
        switch (nRet)
        {
        case WAIT_OBJECT_0:
            {
                va_list args; 
                char cText[MAX_BUFFSIZE] = {0}; 
                va_start(args, pFormat); 
                vsprintf(cText, pFormat, args); 
                OutputLoginfo(cText); 
                va_end(args); 
                ReleaseMutex(m_hMuex);
            }
            break;
        case WAIT_TIMEOUT:
            break;
        case WAIT_FAILED:
            break;
        }
    }

    inline void CLogging::OutputLoginfo( const char *pBuff )
    {
        time_t CurrentTime;
        time(&CurrentTime);
        char TimeBuff[MAX_PATH] = {0};
        char cBuff[MAX_BUFFSIZE] = {0};
        // 根据日志级别的不同配置不同的显示信息
        strftime(TimeBuff, sizeof(TimeBuff), "%H:%M:%S",localtime(&CurrentTime) );
        //sprintf_s(cBuff, MAX_BUFFSIZE, "[%s] [%s: %s] %d entered\n %s", TimeBuff, __FILE__, __FUNCTION__, __LINE__, pBuff);
        sprintf_s(cBuff, MAX_BUFFSIZE, "[%s] [%s:] %d entered\n %s", TimeBuff, __FUNCTION__, __LINE__, pBuff);
        switch (m_LogMode)
        {
        case CONSOLE:
            printf(cBuff);
            break;
        case LOGFILE:
            fputs(cBuff, m_pFile);
            break;
        case CONSOLE_FILE:
            printf(cBuff);
            fputs(cBuff, m_pFile);
            break;
        }
    }
}
StopWatch.h
#pragma once

namespace Logger
{
    class CStopwatch
    {
    private:
        // 64位有符号整数可以用INT64 _int64 LARGE_INTEGER表示
        LARGE_INTEGER m_nPerfFrequency;
        LARGE_INTEGER m_nPerFrefStart;

    public:
        CStopwatch(){QueryPerformanceFrequency(&m_nPerfFrequency); Start();}
        inline void Start(){QueryPerformanceCounter(&m_nPerFrefStart);}

        inline INT64 Now() const
        {
            LARGE_INTEGER nPerfNow;
            QueryPerformanceCounter(&nPerfNow);
            return ((nPerfNow.QuadPart - m_nPerFrefStart.QuadPart) * 1000) / m_nPerfFrequency.QuadPart;
        }

        inline INT64 NowInMicro() const
        {
            LARGE_INTEGER nPerfNow;
            QueryPerformanceCounter(&nPerfNow);
            return ((nPerfNow.QuadPart - m_nPerFrefStart.QuadPart) * 1000000) / m_nPerfFrequency.QuadPart;
        }
    };
}
test.cpp
#include "stdio.h"
#include "Logging.h"
#include "StopWatch.h"
using namespace Logger;

#include <iostream>
using namespace std;


bool bThead1 = false;
bool bThead2 = false;
void TestFun1()
{
    CLogging log(CONSOLE|LOGFILE, "D:\\testlog.txt", "a+");
    for (int i = 0 ; i < 100; i ++)
    {
        log.LogPrintf("%s 0x%0x\n", "hello", i);
    }
}

void TestFun2()
{
    CLogging log(CONSOLE|LOGFILE, "D:\\testlog.txt", "a+");
    for (int i = 101 ; i < 200; i ++)
    {
        log.LogPrintf("%s %0x\n", "hello", i);
    }
}

DWORD WINAPI ThreadFun1(LPVOID lparam)
{
    TestFun1();
    cout << "thread1 end" << endl;
    bThead1 = true;
    return 0;
}
DWORD WINAPI ThreadFun2(LPVOID lparam)
{
    TestFun2();

    cout << "thread2 end" << endl;
    bThead2 = true;
    return 0;
}

int main()
{
    CStopwatch watch;
    //TestFun();  单线程测试
    // 多线程测试
    DWORD lpThreadId1 = 0;
    DWORD lpThreadId2 = 1;
    HANDLE hThread1 = CreateThread(NULL, 0, ThreadFun1, NULL, 0, &lpThreadId1);
    //HANDLE hThread2 = CreateThread(NULL, 0, ThreadFun2, NULL, 0, &lpThreadId2);
    while(1)
    {
        if (bThead1 && bThead2)
        {
            break;
        }
    }
    cout <<"共用时:"<< watch.Now()<<"ms" << endl;
    return 0;
}
posted @ 2018-04-11 09:25  Fate0729  阅读(487)  评论(0编辑  收藏  举报