从新建工程开始使用C++开发单片机(以STM32为例):六、C++输入输出流(附代码)

经过前面几篇文章的铺垫,完成了C语言接口层的GPIO、外部中断、串口、delay等接口,现在可以正式进入C++驱动层的文章。当然C语言接口层的还远没有完成,在以后的文章中还会继续更新。
本文将会介绍一个C++驱动层中非常重要的两个类,outputStream和inputStream,作为输入输出流的基类,为单片机的数据输出提供了统一的接口。在以后的文章中会介绍到的硬件串口类 HardwareUART类和移植的Adafruit的图形类Adafruit_GFX类等都是继承这个类的派生类。这两个类主要参考了Arduino的Print类和Stream类,并进行了修改和扩展。可以满足多种风格的输入输出。
请添加图片描述

一、从Arduino的串口输入输出函数分析输入输出类:

有使用过Arduino的大佬应该对Arduino的输入输出函数很熟悉,先举几个栗子
在这里插入图片描述
1.Arduino的串口输入输出函数

Serial.print("hello world");
Serial.println("hello wrold");
char* str="hello world";
Serial.write(str, strlen(str));

char c;
if(Serial.available())
	c=Serial.read();
......

2.ESP32/8266 TCP服务器或客户端的输入输出函数

client.print("hello world");
client.println("hello wrold");
char* str="hello world";
client.write(str, strlen(str));

char c;
if(client.available())
	c=client.read();

3.Adafruit的OLED库打印字符

display.println("hello wrold");
display.display();

这三个栗子分别操作不同的硬件,而调用的函数却都是一样的print、println、write等。我们再来看看这几个对象的所对应的类:
1.Arduino的串口Serial:
Arduino的串口Serial是类HardwareSerial的实例化对象,这个类的定义和声明可以在arduino安装目录下的hardware\arduino\avr\cores\arduino中找到

class HardwareSerial : public Stream

2.服务器或客户端的输入输出函数
以客户端为例,client对象是类Client的实例化对象,这个类的声明也可以在arduino安装目录下的hardware\arduino\avr\cores\arduino中找到

class Client : public Stream 

3.Adafruit的OLED
oled对象是类Adafruit_SSD1306的实例化对象,Adafruit_SSD1306继承自类Adafruit_GFX,我们来看看Adafruit_GFX的声明

class Adafruit_GFX : public Print

通过上面三个例子,我们发现他们对应的类有两个继承自Stream类,一个继承自Print类,我们再来看看这两个类:

class Stream : public Print
{
  protected:
    unsigned long _timeout;      // number of milliseconds to wait for the next char before aborting timed read
    unsigned long _startMillis;  // used for timeout measurement
    int timedRead();    // read stream with timeout
    int timedPeek();    // peek stream with timeout
    int peekNextDigit(LookaheadMode lookahead, bool detectDecimal); // returns the next numeric digit in the stream or -1 if timeout

  public:
    virtual int available() = 0;
    virtual int read() = 0;
    virtual int peek() = 0;

    Stream() {_timeout=1000;}

// parsing methods

  void setTimeout(unsigned long timeout);  // sets maximum milliseconds to wait for stream data, default is 1 second
  unsigned long getTimeout(void) { return _timeout; }
  
  bool find(char *target);   // reads data from the stream until the target string is found
  bool find(uint8_t *target) { return find ((char *)target); }
  // returns true if target string is found, false if timed out (see setTimeout)

  bool find(char *target, size_t length);   // reads data from the stream until the target string of given length is found
  bool find(uint8_t *target, size_t length) { return find ((char *)target, length); }
  // returns true if target string is found, false if timed out

  bool find(char target) { return find (&target, 1); }

  bool findUntil(char *target, char *terminator);   // as find but search ends if the terminator string is found
  bool findUntil(uint8_t *target, char *terminator) { return findUntil((char *)target, terminator); }

  bool findUntil(char *target, size_t targetLen, char *terminate, size_t termLen);   // as above but search ends if the terminate string is found
  bool findUntil(uint8_t *target, size_t targetLen, char *terminate, size_t termLen) {return findUntil((char *)target, targetLen, terminate, termLen); }

  long parseInt(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
  // returns the first valid (long) integer value from the current position.
  // lookahead determines how parseInt looks ahead in the stream.
  // See LookaheadMode enumeration at the top of the file.
  // Lookahead is terminated by the first character that is not a valid part of an integer.
  // Once parsing commences, 'ignore' will be skipped in the stream.

  float parseFloat(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
  // float version of parseInt

  size_t readBytes( char *buffer, size_t length); // read chars from stream into buffer
  size_t readBytes( uint8_t *buffer, size_t length) { return readBytes((char *)buffer, length); }
  // terminates if length characters have been read or timeout (see setTimeout)
  // returns the number of characters placed in the buffer (0 means no valid data found)

  size_t readBytesUntil( char terminator, char *buffer, size_t length); // as readBytes with terminator character
  size_t readBytesUntil( char terminator, uint8_t *buffer, size_t length) { return readBytesUntil(terminator, (char *)buffer, length); }
  // terminates if length characters have been read, timeout, or if the terminator character  detected
  // returns the number of characters placed in the buffer (0 means no valid data found)

  // Arduino String functions to be added here
  String readString();
  String readStringUntil(char terminator);

  protected:
  long parseInt(char ignore) { return parseInt(SKIP_ALL, ignore); }
  float parseFloat(char ignore) { return parseFloat(SKIP_ALL, ignore); }
  // These overload exists for compatibility with any class that has derived
  // Stream and used parseFloat/Int with a custom ignore character. To keep
  // the public API simple, these overload remains protected.

  struct MultiTarget {
    const char *str;  // string you're searching for
    size_t len;       // length of string you're searching for
    size_t index;     // index used by the search routine.
  };

  // This allows you to search for an arbitrary number of strings.
  // Returns index of the target that is found first or -1 if timeout occurs.
  int findMulti(struct MultiTarget *targets, int tCount);
};
class Print
{
  private:
    int write_error;
    size_t printNumber(unsigned long, uint8_t);
    size_t printFloat(double, uint8_t);
  protected:
    void setWriteError(int err = 1) { write_error = err; }
  public:
    Print() : write_error(0) {}
  
    int getWriteError() { return write_error; }
    void clearWriteError() { setWriteError(0); }
  
    virtual size_t write(uint8_t) = 0;
    size_t write(const char *str) {
      if (str == NULL) return 0;
      return write((const uint8_t *)str, strlen(str));
    }
    virtual size_t write(const uint8_t *buffer, size_t size);
    size_t write(const char *buffer, size_t size) {
      return write((const uint8_t *)buffer, size);
    }

    // default to zero, meaning "a single write may block"
    // should be overriden by subclasses with buffering
    virtual int availableForWrite() { return 0; }

    size_t print(const __FlashStringHelper *);
    size_t print(const String &);
    size_t print(const char[]);
    size_t print(char);
    size_t print(unsigned char, int = DEC);
    size_t print(int, int = DEC);
    size_t print(unsigned int, int = DEC);
    size_t print(long, int = DEC);
    size_t print(unsigned long, int = DEC);
    size_t print(double, int = 2);
    size_t print(const Printable&);

    size_t println(const __FlashStringHelper *);
    size_t println(const String &s);
    size_t println(const char[]);
    size_t println(char);
    size_t println(unsigned char, int = DEC);
    size_t println(int, int = DEC);
    size_t println(unsigned int, int = DEC);
    size_t println(long, int = DEC);
    size_t println(unsigned long, int = DEC);
    size_t println(double, int = 2);
    size_t println(const Printable&);
    size_t println(void);

    virtual void flush() { /* Empty implementation for backward compatibility */ }
};

我们发现Stream类继承自Print类,而我们熟悉的println、print、write等输出函数就在Print中,available()、read()、peak()等输入函数在Stream中。而write、available()、read()、peek()等是纯虚函数,Print类中的和输出有关的函数最后调用的都是write函数,Stream类中和读取有关的函数最后都是调用的read()和peak()。
子类通过实现以上的纯虚函数来实现输入输出函数的重定向。子类所示例化的对象都可以使用这些函数来进行输入和输出。
在使用c语言开发时,我们曾通过重写fputc()函数,来重定向的printf函数。重定向到串口1时就只能输出到串口1,而不能输出到其他的输出设备中。而使用c++类的纯虚函数重定向后,每个实例化的输出设备都有自己的输出函数。并且相比于printf,arduino的println和print通过函数重载提供了更为方便的接口。
由此我们可以对Arduino输入输出流的继承关系用下图来表示:
在这里插入图片描述

二、设计自己的输入输出类

我在之前的文章中说过,我并不是为了全盘移植Arduino的东西。对于Arduino输入输出类暂时无法摸清底层硬件的具体实现,为了自己的底层库兼容,我以Arduino的Stream类和Print类作为参考,自己实现了outputStream和inputStream类,其中既移植了部分Arduino输入输出类中的函数,也增加了自己的思想和风格。
首先是输入输出流的继承关系,我将输入和输出分开,作为两个独立的基类,而不像Arduino把输入类继承自输出类,以此我自己的输入输出类的继承关系如图所示:
在这里插入图片描述
下面我将对这两个类进行说明。

2.1 outputStream
outputStream中有两个纯虚函数成员:

virtual size_t write(uint8_t) = 0;
virtual size_t write(const uint8_t *, size_t) = 0;

这两个纯虚函数分别用于输出一个字节和输出二进制数据。类中的其他的输出函数如print、println等最终调用的都是这两个函数,因此子类只需要将这两个纯虚函数实现,就可以实例化一个输出设备。

在output类中还有两个比较重要的私有成员函数:

	size_t printNumber(unsigned long, uint8_t);
    size_t printFloat(double, uint8_t);

这printNumber()可以将整数以任意进制输出,最高可以使用到16进制。print和println函数通过参数缺省的特性,将默认值设为10进制。printFloat()可以将浮点数以保留任意位小数输出,print和println函数通过参数缺省的特性,设置默认值为保留小数点后2位。print和println输出整数和浮点数都会调用这两个函数。

再来看看outputStream完整声明:

class outputStream
{
private:
    size_t printNumber(unsigned long, uint8_t);
    size_t printFloat(double, uint8_t);

public:
    virtual size_t write(uint8_t) = 0;
    size_t write(const char *str)
    {
        if (str == NULL)
            return 0;
        return write((const uint8_t *)str, strlen(str));
    }
    virtual size_t write(const uint8_t *, size_t) = 0;
    size_t write(const char *buffer, size_t size)
    {
        return write((const uint8_t *)buffer, size);
    }

    // size_t print(const __FlashStringHelper *);
    size_t print(const String &);
    size_t print(const char[]);
    size_t print(char);
    size_t print(unsigned char, int = DEC);
    size_t print(int, int = DEC);
    size_t print(unsigned int, int = DEC);
    size_t print(long long, int = DEC);
    size_t print(unsigned long long, int = DEC);
    size_t print(double, int = 2);
    // size_t print(const Printable &);

    // size_t println(const __FlashStringHelper *);
    size_t println(const String &s);
    size_t println(const char[]);
    size_t println(char);
    size_t println(unsigned char, int = DEC);
    size_t println(int, int = DEC);
    size_t println(unsigned int, int = DEC);
    size_t println(long long, int = DEC);
    size_t println(unsigned long long, int = DEC);
    size_t println(double, int = 2);
    // size_t println(const Printable &);
    size_t println(void) { return print("\r\n"); };

    template <typename T>
    outputStream &operator<<(T out)
    {
        print(out);
        return *this;
    }

    outputStream &operator<<(String &str)
    {
        print(str);
        return *this;
    }
};

首先对比Arduino的输出类Print,Arduino中的write、print、println以及printNumber、printFloat等基本上移植了过来。Print中的flush、availableForWrite以及和error相关的函数在使用时基本上不会用到,所有没有移植。
在我自己的outputStream中添加了如下的成员函数:

	template <typename T>
    outputStream &operator<<(T out)
    {
        print(out);
        return *this;
    }

    outputStream &operator<<(String &str)
    {
        print(str);
        return *this;
    }

通过重载输出运算符<<,使得outputStream类支持了类似c++中std::cout的功能。这是我对Print的一个扩展。

接下来,通过串口输出来展示其功能(硬件串口类HardwareUART继承自outputStream和inputStream,以后会进行介绍)

#include "User.h"

HardwareUART UART(UART_1);
int main()
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	systick_init();
	adc_init();
	/********************************************************************/
	UART.begin(115200);

	int a = 100, pi = PI;
	String str = "hello wold string";

	UART.print("a=");
	UART.print(a);
	UART.print(" 0x");
	UART.println(a, HEX);

	UART.print("pi=");
	UART.print(pi);
	UART.print(" ");
	UART.println(pi, 6);

	UART.print("hello wrold\r\n");
	UART.println("hello wrold");
	UART.println(str);

	UART << "print by \"<<\":" << endl;
	UART << "a=" << a << endl;
	UART << "pi=" << pi << endl;
	UART << "Hello wrold" << endl;
	UART << str << endl;

	UART.println(123);
	UART.println(123.45);
	UART << 123 << " " << 123.45 << endl;
	/********************************************************************/
	while (1)
	{
	}
}

在这里插入图片描述
通过以上示例,大家应该不难发现,使用<<输出运算符会比print和println易用性更强一些。

2.2 inputStream
对于inputStream的设计会比Ardunio的Stream简洁许多,保留了Stream中的available()、read()、peek()。重载了>>输入运算符,使得inputStream子类实例化的输入设备支持c++中类似于std::cin的功能。inputSream大部分的输入函数都是设计为位阻滞函数,没有像Arduino那样可以设置等待时间,在这一点上我嘚inputStream还不够完善,后面还会继续进行修改和扩展。同时inputStream中带有一个缓冲区指针,指向输入设备的接收缓冲区(例如之前的文章中提到的串口接收缓冲区)。

首先介绍成员结构体指针:__rec_buf *buf;
__rec_buf 在之前的文章:从新建工程开始使用C++开发单片机(以STM32为例):五、C语言接口层之串口UART 进行了介绍,这里再具体展现一下:

typedef struct
{
    volatile uint8_t buf[256];      //接收缓冲区
    volatile uint8_t write_index;   //写指针
    volatile uint8_t read_index;    //读指针
    volatile uint16_t data_size;    //缓冲区接收到的数据长度
    /* data */
}__rec_buf;

inputStream中的read、peek、available就是在读取和维护这个缓存区。在串口的类中,将__rec_buf指针指向所对应的串口缓冲区,就可以实现对串口输入的读取。对于例如键盘的类,也可以通过建立上面的缓冲区,将指针指向其缓冲区,实现对键盘输入的读取。

相比于Arduino的Stream强调查找,例如和find相关的函数,inputStream更加强调多样化的扫描输入,因此我并没有移植Stream中find相关的函数。对于find我更偏向于把它放在String字符串类中。inputStream类有许多的scan函数,和outputStream中的print相对应,用于实现类似于C语言中scanf的功能。并且设计了scanNumber()和scanFloat()对应outputSream中的printNumber()和printFloat(),大部分的scan数字的函数底层都是scanNumber()和scanFloat()。

再来看看outputStream完整声明:

class inputStream
{
protected:
    __rec_buf *buf;

public:
    virtual int available();
    virtual int read();
    virtual int peek();

    /***阻滞读取**********************/
    long scanNumber();
    double scanFloat();

    int getc();

    int scan(char *); //扫描字符串
    int scan(String &);
    int scan(char &);
    int scan(unsigned char &);      //扫描字符
    int scan(int &);                //扫描数字
    int scan(unsigned int &);       //扫描数字
    int scan(long long &);          //扫描数字
    int scan(unsigned long long &); //扫描数字
    int scan(double &);             //扫描浮点数
    int scan(float &);              //扫描浮点数

    template <typename T>
    inputStream &operator>>(T &out)
    {
        scan(out);
        return *this;
    }
};

相比于Arduino的Stream是不是简单了很多?
在scan的基础上,重载了>>输入运算符,和outputStream中的<<输出运算符相对应,增加了易用性。

	template <typename T>
    inputStream &operator>>(T &out)
    {
        scan(out);
        return *this;
    }

接下来这段代码用来展示其功能:

#include "User.h"

HardwareUART UART(UART_1);
int main()
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	systick_init();
	adc_init();
	/********************************************************************/

	UART.begin(115200);
	int a;
	float f;
	String str;
	char buf[100];

	UART << "scan example:" << endl;
	UART.scan(a);
	UART.scan(f);
	UART.scan(str);
	UART.scan(buf);
	UART << "a=" << a << " f=" << f << " String is:" << str << " buf is" << buf << endl;

	UART << ">> example:" << endl;
	UART >> a >> f >> str >> buf;
	UART << "a=" << a << " f=" << f << " String is:" << str << " buf is" << buf;
	/********************************************************************/
	while (1)
	{
	}
}

串口输入为:
11 11.35 str buf
22,3.14 hello,world hello
串口输出:
在这里插入图片描述

三、总结

这两个类是我花费大量时间所设计出来的。在目前我已完成的内容中,硬件串口、屏幕输出、AT24C04等外设和外部硬件设备的驱动类都继承了这两个类,实践证明所花的时间也是值得的。相比于C语言的sprintf、printf以及直接调用C语言层的接口,使用类来封装会占用不少的单片机资源从而影响到执行的效率,不过对于现在的内存高达几百k甚至几M的单片机来说,这些效率和内存的牺牲换来的更高效率的开发是远远值得的。在上面的例子中,有使用到了String类,这是我从Arduino移植过来的WString,非常的好用,后面的文章将会介绍到。
在这里插入图片描述

附:完整代码

/*file : inputStream.h*/
#ifndef __OUTPUT_STREAM_H
#define __OUTPUT_STREAM_H

#include <stdio.h>
#include "WString.h"
#include "headfile.h"

#define DEC 10
#define HEX 16
#define OCT 8
#ifdef BIN // Prevent warnings if BIN is previously defined in "iotnx4.h" or similar
#undef BIN
#endif
#define BIN 2

//using std::endl;
#define endl "\r\n"

class outputStream
{
private:
    size_t printNumber(unsigned long, uint8_t);
    size_t printFloat(double, uint8_t);

public:
    virtual size_t write(uint8_t) = 0;
    size_t write(const char *str)
    {
        if (str == NULL)
            return 0;
        return write((const uint8_t *)str, strlen(str));
    }
    virtual size_t write(const uint8_t *, size_t) = 0;
    size_t write(const char *buffer, size_t size)
    {
        return write((const uint8_t *)buffer, size);
    }

    // size_t print(const __FlashStringHelper *);
    size_t print(const String &);
    size_t print(const char[]);
    size_t print(char);
    size_t print(unsigned char, int = DEC);
    size_t print(int, int = DEC);
    size_t print(unsigned int, int = DEC);
    size_t print(long long, int = DEC);
    size_t print(unsigned long long, int = DEC);
    size_t print(double, int = 2);
    // size_t print(const Printable &);

    // size_t println(const __FlashStringHelper *);
    size_t println(const String &s);
    size_t println(const char[]);
    size_t println(char);
    size_t println(unsigned char, int = DEC);
    size_t println(int, int = DEC);
    size_t println(unsigned int, int = DEC);
    size_t println(long long, int = DEC);
    size_t println(unsigned long long, int = DEC);
    size_t println(double, int = 2);
    // size_t println(const Printable &);
    size_t println(void) { return print("\r\n"); };

    template <typename T>
    outputStream &operator<<(T out)
    {
        print(out);
        return *this;
    }

    outputStream &operator<<(String &str)
    {
        print(str);
        return *this;
    }
};

#endif

/*file : outputStream.cpp*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdarg.h>

#include "outputStream.h"

// Public Methods //

/* default implementation: may be overridden */

size_t outputStream::print(const String &s)
{
  return write(s.c_str(), s.length());
}

size_t outputStream::print(const char str[])
{
  return write(str);
}

size_t outputStream::print(char c)
{
  return write(c);
}

size_t outputStream::print(unsigned char b, int base)
{
  return print((unsigned long long)b, base);
}

size_t outputStream::print(int n, int base)
{
  return print((long long)n, base);
}

size_t outputStream::print(unsigned int n, int base)
{
  return print((unsigned long long)n, base);
}

size_t outputStream::print(long long n, int base)
{
  if (base == 0)
  {
    return write(n);
  }
  else if (base == 10)
  {
    if (n < 0)
    {
      int t = print('-');
      n = -n;
      return printNumber(n, 10) + t;
    }
    return printNumber(n, 10);
  }
  else
  {
    return printNumber(n, base);
  }
}

size_t outputStream::print(unsigned long long n, int base)
{
  if (base == 0)
    return write(n);
  else
    return printNumber(n, base);
}

size_t outputStream::print(double n, int digits)
{
  return printFloat(n, digits);
}

size_t outputStream::println(const String &s)
{
  size_t n = print(s);
  n += println();
  return n;
}

size_t outputStream::println(const char c[])
{
  size_t n = print(c);
  n += println();
  return n;
}

size_t outputStream::println(char c)
{
  size_t n = print(c);
  n += println();
  return n;
}

size_t outputStream::println(unsigned char b, int base)
{
  size_t n = print(b, base);
  n += println();
  return n;
}

size_t outputStream::println(int num, int base)
{
  size_t n = print(num, base);
  n += println();
  return n;
}

size_t outputStream::println(unsigned int num, int base)
{
  size_t n = print(num, base);
  n += println();
  return n;
}

size_t outputStream::println(long long num, int base)
{
  size_t n = print(num, base);
  n += println();
  return n;
}

size_t outputStream::println(unsigned long long num, int base)
{
  size_t n = print(num, base);
  n += println();
  return n;
}

size_t outputStream::println(double num, int digits)
{
  size_t n = print(num, digits);
  n += println();
  return n;
}

// Private Methods /

size_t outputStream::printNumber(unsigned long n, uint8_t base)
{
  char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.
  char *str = &buf[sizeof(buf) - 1];

  *str = '\0';

  // prevent crash if called with base == 1
  if (base < 2)
    base = 10;

  do
  {
    char c = n % base;
    n /= base;

    *--str = c < 10 ? c + '0' : c + 'A' - 10;
  } while (n);

  return write(str);
}

size_t outputStream::printFloat(double number, uint8_t digits)
{
  size_t n = 0;

  if (isnan(number))
    return print("nan");
  if (isinf(number))
    return print("inf");
  if (number > 4294967040.0)
    return print("ovf"); // constant determined empirically
  if (number < -4294967040.0)
    return print("ovf"); // constant determined empirically

  // Handle negative numbers
  if (number < 0.0)
  {
    n += print('-');
    number = -number;
  }

  // Round correctly so that print(1.999, 2) prints as "2.00"
  double rounding = 0.5;
  for (uint8_t i = 0; i < digits; ++i)
    rounding /= 10.0;

  number += rounding;

  // Extract the integer part of the number and print it
  unsigned long long int_part = (unsigned long long)number;
  double remainder = number - (double)int_part;
  n += print(int_part);

  // Print the decimal point, but only if there are digits beyond
  if (digits > 0)
  {
    n += print('.');
  }

  // Extract digits from the remainder one at a time
  while (digits-- > 0)
  {
    remainder *= 10.0;
    unsigned int toPrint = (unsigned int)(remainder);
    n += print(toPrint);
    remainder -= toPrint;
  }

  return n;
}


/*file : inputStream.h*/
#ifndef __INPUT_STREAM_H
#define __INPUT_STREAM_H

#include "WString.h"
#include "headfile.h"

class inputStream
{
protected:
    __rec_buf *buf;

public:
    virtual int available();
    virtual int read();
    virtual int peek();

    /***阻滞读取**********************/
    long scanNumber();
    double scanFloat();

    int getc();

    int scan(char *); //扫描字符串
    int scan(String &);
    int scan(char &);
    int scan(unsigned char &);      //扫描字符
    int scan(int &);                //扫描数字
    int scan(unsigned int &);       //扫描数字
    int scan(long long &);          //扫描数字
    int scan(unsigned long long &); //扫描数字
    int scan(double &);             //扫描浮点数
    int scan(float &);              //扫描浮点数

    template <typename T>
    inputStream &operator>>(T &out)
    {
        scan(out);
        return *this;
    }
};

#endif

/*file : inputStream.cpp*/
#include "inputStream.h"
#include "math.h"
int inputStream::available()
{
    return buf->data_size;
}

int inputStream::read()
{
    if (buf->data_size == 0)
    {
        return -1;
    }
    buf->data_size--;
    return buf->buf[buf->read_index++];
}

int inputStream::peek()
{
    if (buf->data_size == 0)
    {
        return -1;
    }
    return buf->buf[buf->read_index];
}

int inputStream::getc()
{
    while (!available())
        ;
    return read();
}

long inputStream::scanNumber()
{
    char ch;
    do
    {
        ch = getc();
    } while (ch != '-' && (ch <= '0' || ch >= '9'));

    long res;
    int fu_flag = 0;

    if (ch == '-')
    {
        res = 0;
        fu_flag = 1;
    }
    else
    {
        res = ch - '0';
    }

    ch = getc();
    while (ch >= '0' && ch <= '9')
    {
        res *= 10;
        res += (ch - '0');
        ch = getc();
    }

    if (fu_flag)
        res = -res;
    return res;
}

double inputStream::scanFloat()
{
    char ch;
    do
    {
        ch = getc();
    } while (ch != '-' && (ch <= '0' || ch >= '9'));

    double res;
    int fu_flag = 0;

    if (ch == '-')
    {
        res = 0;
        fu_flag = 1;
    }
    else
    {
        res = (double)(ch - '0');
    }

    ch = getc();
    while (ch >= '0' && ch <= '9')
    {
        res *= 10.0;
        res += (double)(ch - '0');
        ch = getc();
    }

    if (ch != '.')
    {
        if (fu_flag)
            res = -res;
        return res;
    }

    double d = 0.1;

    while (1)
    {
        ch = getc();
        if (ch < '0' || ch > '9')
            break;
        res += d * (double)(ch - '0');
        d /= 10.0;
    }
    if (fu_flag)
        res = -res;
    return res;
}

int inputStream::scan(char &in)
{
    in = getc();
    return in;
}

int inputStream::scan(unsigned char &in)
{
    in = scanNumber();
    return in;
}

int inputStream::scan(int &in)
{
    in = scanNumber();
    return in;
}

int inputStream::scan(unsigned int &in)
{
    in = scanNumber();
    return in;
}

int inputStream::scan(long long &in)
{
    in = scanNumber();
    return in;
}

int inputStream::scan(unsigned long long &in)
{
    in = scanNumber();
    return in;
}

int inputStream::scan(double &in)
{
    in = scanFloat();
    return (int)in;
}

int inputStream::scan(float &in)
{
    in = scanFloat();
    return (int)in;
}

int inputStream::scan(char *in)
{
    char ch;
    do
    {
        ch = getc();
    } while (ch <= ' ' || ch > '~');

    int pos = 0;
    in[pos++] = ch;
    while (1)
    {
        ch = getc();
        if (ch <= ' ' || ch > '~')
        {
            break;
        }
        in[pos++] = ch;
    }
    in[pos] = '\0';
    return pos;
}

int inputStream::scan(String &in)
{
    in = "";
    char ch;
    do
    {
        ch = getc();
    } while (ch <= ' ' || ch > '~');
    in += ch;
    while (1)
    {
        ch = getc();
        if (ch <= ' ' || ch > '~')
        {
            break;
        }
        in += ch;
    }
    return in.length();
}

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值