從新建工程開始使用C++開發單片機(以STM32為例):六、C++輸入輸出流(附代碼)

SiO2愛上HF 2022-01-07 16:22:38 阅读数:105

新建 建工 工程 使用 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();
}
版权声明:本文为[SiO2愛上HF]所创,转载请带上原文链接,感谢。 https://gsmany.com/2022/01/202201071622370594.html