IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【原创】C++数据bit比特位编辑类CLBit -> 正文阅读

[C++知识库]【原创】C++数据bit比特位编辑类CLBit

C++数据bit比特位编辑类CLBit

目前数据的最小编辑单位大多为Byte(比特),而缺少对bit(比特位)的编辑方式。因此CLBit类就是为了编辑数据比特位构造的包装类。通过比特位级别的数据编辑(增删改查等)功能,可更有效的使用内存空间资源。

#ifndef __CL_BITBASE_H__
#define __CL_BITBASE_H__

#include "../_cl_common/CLCommon.h"
#include <vector>
#include <iostream>
#include <type_traits>

//比特位(Bit)读写工具类
class CLBit :std::vector<unsigned char> {
	typedef std::vector<unsigned char> base;
	typedef unsigned char Byte;
	typedef unsigned long long Var64;
	typedef CLBit& ref;
	bool _checkBuf(size_t bitCounts, size_t startPos){
		if (bitCounts) {
			auto si = (((startPos + bitCounts - 1) >> 3) + 1);
			if (base::size() < si)base::resize(si, 0);
			return true;
		}
		else return false;
	}
	static bool _getBit(const void* const src, size_t bitPos) {
		return (*(((Byte*)src) + (bitPos >> 3))) & (Byte(0x1) << (bitPos & 0x7));
	}
	static void _setBit(void* const src, size_t bitPos,bool val)  {
		if (val)
			(*(((Byte*)src) + (bitPos >> 3))) |= (Byte(0x1) << (bitPos & 0x7));//置为1
		else
			(*(((Byte*)src) + (bitPos >> 3))) &= (~(Byte(0x1) << (bitPos & 0x7)));//置为0
	}	
	//bit比特位位标包装类
	struct Bit {
		void* const _data;
		const size_t _pos;
		operator bool() const {
			return CLBit::_getBit(_data, _pos);
		}
		const Bit& operator=(bool val) const {
			CLBit::_setBit(_data, _pos, val);
			return *this;
		}
		const Bit& operator=(const Bit& val) const {
			CLBit::_setBit(_data, _pos, CLBit::_getBit(val._data,val._pos));
			return *this;
		}
		const Bit& operator|=(bool val) const {
			CLBit::_setBit(_data, _pos, CLBit::_getBit(_data, _pos) || val);
			return *this;
		}
		const Bit& operator&=(bool val) const {
			CLBit::_setBit(_data, _pos, CLBit::_getBit(_data, _pos) && val);
			return *this;
		}
		const Bit& operator^=(bool val) const {
			CLBit::_setBit(_data, _pos, CLBit::_getBit(_data, _pos) ^ val);
			return *this;
		}
		const Bit& operator~() const {
			CLBit::_setBit(_data, _pos, !CLBit::_getBit(_data, _pos));
			return *this;
		}
	};
public:
	CLBit() {}
	//用一个数据区src前srcbitCounts个比特位的值设置对象的第insertPos位bit开始的位。
	template<class T>
	explicit CLBit(T* src, size_t srcbitCounts, size_t insertPos) {
		setValue(src, srcbitCounts, 0, insertPos);
	}
	//保存(只包含0、1)字符串(例如:"101001100111001010")描述的二进制值到指定位置。字符串只识别0、1字符,若包含其他字符则忽略该字符位。
	//其中isbitCharStringBigEnd指明字符串是否是大端顺序记录的,默认下是小端记录。
	CLBit(PCStr charString, size_t pos = 0, bool isbitCharStringBigEnd = false) {
		setValueByChar2(charString, pos, isbitCharStringBigEnd);
	}
	//用一个值src各比特位的值设置对象的第insertPos位bit开始的位。
	template<class T>
	CLBit(T src, size_t insertPos = 0) {
		static_assert(std::is_integral_v<T> || std::is_floating_point_v<T> ,
			"template<class T>CLBit(T src, size_t insertPos): class T must be integral or floating point!");
		setValue((const void*)&src, sizeof(T) * 8, insertPos);
	}
	//用一个Bit比特位的值0或1设置对象的第insertPos位bit。
	explicit CLBit(const Bit& bt, size_t insertPos = 0) {
		bool v = bt;
		setValue((const void*)&v, 1, insertPos);
	}
	//范围pos比特位的值0或1。
	bool operator[](size_t pos) const {
		return _getBit(data(),pos);
	}
	//返回一个Bit类型,Bit是一个可以设置bit位值的辅助对象。
	Bit operator[](size_t pos) {
		_checkBuf(1, pos);
		return Bit{data(),pos};
	}	
	//清空数据
	ref clear() {
		base::clear();
		return *this;
	}
	//保存的比特位长度
	size_t size() const {
		return base::size() * 8;
	}
	//预分配多少个比特位空间
	ref reserve(size_t reserveBitCounts) {
		return _checkBuf(reserveBitCounts,0), * this;
	}
	//按0/1字符串打印保存数据内容。st=0表示从起始索引,ed=0表示显示到结束索引,eclapse表示多少个0/1字符后输出一个分割占位空格。
	void print(size_t st = 0, size_t ed = 0, size_t eclapse = 8) const {
		auto _st = min(st, size());
		auto _ed = ed <= 0 ? size() :
			ed < _st ? _st : ed > size() ? size() : ed;
		size_t time = eclapse;
		for (size_t i = _st; i < _ed; i++) {
			cout << (*this)[i];
			if (time)
				if (--time == 0) {
					cout << " "; time = eclapse;
				}
		}
	}
	//目标数据区src从srcBitStartPos开始的前srcBitCount个比特位内容,设置到起始由insertPos指定的比特位。
	template<class T>
	ref setValue(T* src, size_t srcBitCount, size_t srcBitStartPos, size_t insertPos) {
		if (_checkBuf(srcBitCount, insertPos)) {
			for (size_t i = 0; i < srcBitCount; i++)
				_setBit(data(), insertPos + i, _getBit(src, srcBitStartPos + i));
		}
		else throw std::invalid_argument("CLBit::setValue: para \'srcBitCount\' con not be 0!");
		return *this;
	}
	//目标数据区src开始的前srcBitCount个比特位内容,设置到起始由insertPos指定的比特位。
	template<class T>
	ref setValue(T* src,size_t srcBitCount, size_t insertPos) {		
		return setValue(src, srcBitCount, 0, insertPos);
	}
	//目标值src开始的前srcBitCount个比特位内容,设置到起始由insertPos指定的比特位。
	template<class T>
	ref setValue(T src, size_t srcBitCount = 0, size_t insertPos = 0) {
		static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>,
			"template<class T>setValue(T src, size_t srcBitCount, size_t insertPos): class T must be integral or floating point!");
		return	setValue((const void*)&src, srcBitCount ? min(sizeof(T) * 8, srcBitCount) : sizeof(T) * 8, insertPos);
	}	
	//目标比特对象bit从srcBitStartPos开始的前srcBitCount个比特位内容,设置到起始由insertPos指定的比特位。
	ref setValue(const CLBit& bit, size_t srcBitCount, size_t srcBitStartPos, size_t insertPos) {
		if (_checkBuf(srcBitCount, insertPos)) {
			for (size_t i = 0; i < srcBitCount; i++)
				_setBit(data(), insertPos + i, bit[srcBitStartPos + i]);
		}
		else throw std::invalid_argument("CLBit::setValue: para \'srcBitCount\' con not be 0!");
		return *this;
	}
	//取得保存的数据区从pos开始的前getBitCount个比特位内容,返回到一个最大64位的Var64类型中。
	//因此,getBitCount不能大于64,当大于64时刻超出部分会被舍弃。若需要取得大于64位的内容,需使用3参数版本。
	Var64 getValue(size_t getBitCount, size_t pos) const {
		Var64 _var = 0;
		getValue(&_var, min(sizeof(Var64) * 8, getBitCount), pos);
		return _var;
	}
	//取得保存的数据区从pos开始的前getBitCount个比特位内容,返回到一个最大64位的Var64类型中。
	//因此,getBitCount不能大于64,当大于64时刻超出部分会被舍弃。若需要取得大于64位的内容,需使用3参数版本。
	Var64 operator()(size_t getBitCount, size_t pos) const {
		return getValue(getBitCount, pos);
	}
	//取得保存的数据区从pos开始的前getBitCount个比特位内容,返回到一个buf指向的空间(按低位字节序保存的任意类型)。
	//返回值为buf指向的空间本身。
	void* getValue(void* const buf,size_t getBitCount, size_t pos) const {
		for (size_t i = 0; i < getBitCount; i++)
			_setBit(buf, i, _getBit(data(), pos + i));
		return buf;
	}
	//取得保存的数据区从pos开始的前getBitCount个比特位内容,返回到一个buf指向的空间(按低位字节序保存的任意类型)。
	//返回值为buf指向的空间本身。
	void* operator()(void* const buf, size_t getBitCount, size_t pos) const {
		getValue(buf, getBitCount, pos);
	}	
	//保存(只包含0、1)字符串(例如:"101001100111001010")描述的二进制值到指定位置。字符串只识别0、1字符,若包含其他字符则忽略该字符位。
	//其中isbitCharStringBigEnd指明字符串是否是大端顺序记录的,默认下是小端记录。
	ref setValueByChar2(PCStr bitCharString, size_t pos,bool isbitCharStringBigEnd = false) {
		if (bitCharString) {
			if (isbitCharStringBigEnd) {
				PCStr pend = bitCharString;
				while (*pend)pend++; pend--;
				for (; pend >= bitCharString; pend--)
				{
					if (*pend == _T('0')) 
						(*this)[pos++] = 0;					
					else if (*pend == _T('1')) 
						(*this)[pos++] = 1;					
				}
			}
			else {
				for (; *bitCharString; bitCharString++)
				{
					if (*bitCharString == _T('0')) 
						(*this)[pos++] = 0;					
					else if (*bitCharString == _T('1')) 
						(*this)[pos++] = 1;					
				}
			}
		}return *this;
	}
	//保存(只包含0-3)字符串(例如:"321032103210")描述的四进制值到指定位置。字符串只识别0-3字符,若包含其他字符则忽略该字符位。
	//其中isbitCharStringBigEnd指明字符串是否是大端顺序记录的,默认下是小端记录。
	ref setValueByChar4(PCStr bitCharString, size_t pos, bool isbitCharStringBigEnd = false) {
		if (bitCharString) {
			if (isbitCharStringBigEnd) {
				PCStr pend = bitCharString;
				while (*pend)pend++; pend--;
				for (; pend >= bitCharString; pend--)
				{
					Char ch = *pend;
					if (ch >= _T('0') && ch <= _T('3')) {
						ch -= 48;
						setValue(ch, 2, pos);
						pos += 2;
					}
				}
			}
			else {
				for (; *bitCharString; bitCharString++)
				{
					Char ch = *bitCharString;
					if (ch >= _T('0') && ch <= _T('3')) {
						ch -= 48;
						setValue(ch, 2, pos);
						pos += 2;
					}
				}
			}
		}
		return *this;
	}
	//保存(只包含0-7)字符串(例如:"765432107654321076543210")描述的八进制值到指定位置。字符串只识别0-7字符,若包含其他字符则忽略该字符位。
	//其中isbitCharStringBigEnd指明字符串是否是大端顺序记录的,默认下是小端记录。
	ref setValueByChar8(PCStr bitCharString, size_t pos, bool isbitCharStringBigEnd = false) {
		if (bitCharString) {
			if (isbitCharStringBigEnd) {
				PCStr pend = bitCharString;
				while (*pend)pend++; pend--;
				for (; pend >= bitCharString; pend--)
				{
					Char ch = *pend;
					if (ch >= _T('0') && ch <= _T('7')) {
						ch -= 48;
						setValue(ch, 3, pos);
						pos += 3;
					}
				}
			}
			else {
				for (; *bitCharString; bitCharString++)
				{
					Char ch = *bitCharString;
					if (ch >= _T('0') && ch <= _T('7')) {
						ch -= 48;
						setValue(ch, 3, pos);
						pos += 3;
					}
				}
			}
		}
		return *this;
	}
	//保存(只包含0-F/f)字符串(例如:"FfDCBA76543210FEDCBA7654321076543210")描述的十六进制值到指定位置。
	//字符串只识别0-F/f字符,不区分大小写,若包含其他字符则忽略该字符位。
	//其中isbitCharStringBigEnd指明字符串是否是大端顺序记录的,默认下是小端记录。
	ref setValueByChar16(PCStr bitCharString, size_t pos, bool isbitCharStringBigEnd = false) {
		if (bitCharString) {
			if (isbitCharStringBigEnd) {
				PCStr pend = bitCharString;
				while (*pend)pend++; pend--;
				for (; pend >= bitCharString; pend--)
				{
					Char ch = *pend;
					if (ch >= _T('0') && ch <= _T('9')) {
						ch -= 48;
						setValue(ch, 4, pos);
						pos += 4;
					}
					else if (ch >= _T('a') && ch <= _T('f')) {
						ch -= 87;
						setValue(ch, 4, pos);
						pos += 4;
					}
					else if (ch >= _T('A') && ch <= _T('F')) {
						ch -= 55;
						setValue(ch, 4, pos);
						pos += 4;
					}
				}
			}
			else {
				for (; *bitCharString; bitCharString++)
				{
					Char ch = *bitCharString;
					if (ch >= _T('0') && ch <= _T('9')) {
						ch -= 48;
						setValue(ch, 4, pos);
						pos += 4;
					}
					else if (ch >= _T('a') && ch <= _T('f')) {
						ch -= 87;
						setValue(ch, 4, pos);
						pos += 4;
					}
					else if (ch >= _T('A') && ch <= _T('F')) {
						ch -= 55;
						setValue(ch, 4, pos);
						pos += 4;
					}
				}
			}
		}
		return *this;
	}
	//插入原对象src从srcBitStartPos开始的srcBitCounts个比特位数据到对象insertPos的位置之前。
	ref insertBit(const CLBit& src, size_t srcBitCounts, size_t srcBitStartPos, size_t insertPos) {
		if (_checkBuf(srcBitCounts, insertPos)) {
			size_t i = size();
			for (; i > insertPos;)
			{
				i--;
				if (_getBit(data(), i))
					break;
			}
			for (; i >= insertPos; i--)			
				_setBit(data(), i + srcBitCounts, _getBit(data(), i));			
			for ( i = 0; i < srcBitCounts; i++)			
				_setBit(data(), insertPos + i, src[srcBitStartPos + i]);			
		}
		return *this;
	}
	//删除对象从deleteStartPos开始的bitCountsToDelete个比特位数据,后面的数据前移。
	ref deleteBit(size_t bitCountsToDelete, size_t deleteStartPos) {
		for (size_t i = deleteStartPos;	i < size(); i++)		
			_setBit(data(), i, i + bitCountsToDelete < size() ? _getBit(data(), i + bitCountsToDelete) : false);		
		return *this;
	}
	//擦除对象从eraseStartPos开始的bitCountsToErase个比特位数据,该范围的bit位全部置0
	ref erase(size_t bitCountsToErase, size_t eraseStartPos) {
		for (size_t i = eraseStartPos, si = min(size(), eraseStartPos + bitCountsToErase); i < si; i++)
			_setBit(data(), i, false);
		return *this;
	}
	//填充对象从fillStartPos开始的bitCountsToFill个比特位数据,该范围的bit位全部置1
	ref fill(size_t bitCountsToErase, size_t fillStartPos) {
		if (_checkBuf(bitCountsToErase, fillStartPos)) {
			for (size_t i = fillStartPos, si = fillStartPos + bitCountsToErase; i < si; i++)
				_setBit(data(), i, true);
		}
		return *this;
	}
	//读取一个文件的指定长度的在指定位置的bit比特位数据到对象中的指定位置中。
	//file已经打开的可读文件流,loadBitCouunts读取比特数(为0表示读取全部文件bit数据),
	//loadStartBitPos文件读取的起始bit位置,insertBitPos读取数据起始插入bit位置,chearBeforeLoad读取前是否先清理对象;
	//返回已读取的bit位数;
	template<class _iofstream = std::ifstream>
	size_t loadFile(_iofstream& file,size_t loadBitCounts = 0, size_t loadStartBitPos = 0,
		size_t insertBitPos = 0,bool chearBeforeLoad = true) {
		static_assert(std::is_same_v<_iofstream, std::fstream> || std::is_same_v<_iofstream, std::ifstream>,
			"template <class _iofstream> loadFile:_iofstream is not std::fstream or std::ifstream!");
		if (chearBeforeLoad)
			clear();
		if (!file.is_open())
			return 0;
		size_t fileSize = file.seekg(0, file.end).tellg();
		auto fileBits = loadBitCounts == 0 ? fileSize * 8 : min(loadBitCounts, fileSize * 8);
		auto maxBits = min(fileSize * 8, loadStartBitPos + fileBits) - min(fileSize * 8, loadStartBitPos);
		char ch = 0; size_t total = 0;
		if (maxBits) {
			auto startBytePos = min(fileSize, (loadStartBitPos >> 3));
			auto endBytePos = min(fileSize, ((loadStartBitPos + maxBits) >> 3) + 1);
			auto si = (((loadStartBitPos + maxBits - 1) >> 3) + 1);
			if (base::size() < si)base::resize(si, 0);
			file.seekg(ios::beg, startBytePos);
			for (size_t i = startBytePos; i < endBytePos; i++)
			{
				file.read(&ch, 1);
				for (size_t j = (loadStartBitPos & 0x7); j < 8; j++, loadStartBitPos++, insertBitPos++)
				{
					_setBit(data(), insertBitPos, _getBit(&ch, j));
					if (++total >= maxBits)
						goto end;
				}
			}
		}
	end:
		return total;
	}
	//写入在指定位置的指定长度个bit比特位数据到文件流中。
	//file已经打开的可写文件流,写入位置应该已经被设置,srcBitCountsToWrite写入的比特数(为0表示写入全部bit数据),
	//srcStartBitPosToWrite要写入文件的原数据的起始bit位置;
	//返回已写入的bit位数;
	template<class _iofstream = std::ofstream>
	size_t writeToFile(_iofstream& file, size_t srcBitCountsToWrite = 0, size_t srcStartBitPosToWrite = 0) {
		static_assert(std::is_same_v<_iofstream, std::fstream> || std::is_same_v<_iofstream, std::ofstream>,
			"template <class _iofstream> writeToFile:_iofstream is not std::fstream or std::ofstream!");
		if (!file.is_open())
			return 0;
		auto fileBits = srcBitCountsToWrite == 0 ? size() : min(srcBitCountsToWrite, size());
		auto maxBits = min(size(), srcStartBitPosToWrite + fileBits) - min(size(),srcStartBitPosToWrite);
		char ch = 0; size_t total = 0;
		while (1)
		{
			ch = 0;
			for (size_t j = (srcStartBitPosToWrite & 0x7); j < 8; j++, srcStartBitPosToWrite++)
			{
				_setBit(&ch, j, _getBit(data(), srcStartBitPosToWrite));
				if (++total >= maxBits) {
					file.write(&ch, 1);
					goto end;
				}
			}
			file.write(&ch, 1);
		}
	end:
		return total;
	}
};

#endif

测试代码

void test_class_CLBit() {
	cout << "\n\ntest_class_CLBit:\n\n";
	srand(time(0));
	CLBit bt;
	bt = 1123;
	bt[8] = 1;
	bt[123] = 1;
	bt[0] = 1;
	bt[1] = 1;
	bt[2] = 1;
	bt.clear();
	((bt[5] = 1, bt)[3] = 2, bt)[13] = 2;
	cout << endl;;
	for (size_t i = 0; i < 10000000; i++)
	{
		int v = (rand()%20) > 10 ? 1 : 0;
		bt[i] = v;
		if(i < 200)
			cout << v;;
	}
	cout << endl;; cout << endl;; cout << endl;;
	for (size_t i = 0; i < 10000000; i++)
	{		
		if (i < 200)
			cout << bt[i];;
	}
	cout << endl;; cout << endl;; cout << endl;;
	struct _A {
		_A(const CLBit& bt) {
			for (size_t i = 0; i < 10000000; i++)
			{
				if (i < 200)
					cout << bt[i];
			}
		}
	};	
	_A a(bt);
	cout << "\n\n";
	bt.print(0,200,8);
	cout << "\n\n";
	const auto v = bt.getValue(20, 30);
	CLBit btv; btv.setValue(&v,64,0); btv.print(0, 200);
	cout << "\n\n";
	byte d[20] = { 0 };
	bt.getValue(d, 20 * 8, 20);
	CLBit btd; btd.setValue(d, 20 * 8,0); btd.print(0, 200);
	char v1 = 34; int v2 = 3456; long long v3 = 2135; double v4 = 65659.362;
	CLBit btv1 (v1); cout << "\n\n"; btv1.print(0, 200);
	CLBit btv11(v1, 3); 
	btv11.setValue(v3, 0, 0); 
	cout << "\n\n"; btv11.print(0, 200);
	CLBit btv13(&v1,16,8); cout << "\n\n"; btv13.print(0, 200);
	CLBit btv2(v2); cout << "\n\n"; btv2.print(0, 200);
	CLBit btv3(v3); cout << "\n\n"; btv3.print(0, 200);
	CLBit btv4(v4); cout << "\n\n"; btv4.print(0, 200);
	CLBit btv7 = btv4; 
	cout << "\n\n"; btv7.print(0, 200);
	CLBit btv8 (btv7.getValue(11, 11));
	cout << "\n\n"; btv8.print(0, 200);
	CLBit btv9((short)btv7(11, 11));
	cout << "\n\n"; btv9.print(0, 200);
	CLBit btv19(btv7[1],3);
	cout << "\n\n"; btv19.print(0, 200);
	btv19[20] = btv7[1];
	btv19[21] = 2;
	btv19[28] &= 1;
	btv19[29] ^= 1;
	~btv19[33] ^= 0;
	cout << "\n\n"; btv19.print(0, 200);
	CLBit btv112; btv112.setValueByChar2("1111111111111111", 5);
	cout << "\n\n"; btv112.print(0, 200);
	CLBit btv111; btv111.setValueByChar16("A0a0", 8);
	cout << "\n\n"; btv111.print(0, 200);
	CLBit btv14; btv14.setValueByChar16("FaaCBA7654321000000A765432107654aaff", 8);
		cout << "\n\n"; btv14.print(0, 0, 8);
	CLBit btv15; btv15.setValueByChar16("aaaa", 8,true);
		cout << "\n\n"; btv15.print(0, 0, 8);
	btv112.insertBit(CLBit("010010"), 6, 0, 11); cout << "\n\n"; btv112.print(0, 0, 8);
	btv112.deleteBit(5,7); cout << "\n\n"; btv112.print(0, 0, 8);
	CLBit bt1 = 5625; cout << "\n\n\n"; bt1.print(0, 0, 8);
	CLBit bt2 = 5625123.123; cout << "\n\n\n"; bt2.print(0, 0, 8);
	CLBit bt3 = "001000101111101111110000111001001101011101010100111110011110111101111111111100111000001111111111111100011111111111110101100"; cout << "\n\n\n"; bt3.print(0, 0, 8);
	ofstream f1(CLString().getSpecialFolderPath() << CLSla << "bttem.txt");
	bt3.writeToFile(f1);
	f1.close();
	ifstream f2(CLString().getSpecialFolderPath() << CLSla << "bttem.txt");
	//f2.seekg(f2.beg);
	CLBit bt4;
	bt4.loadFile(f2); cout << "\n\n\n"; bt4.print(0, 0, 8);
}

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-01 14:19:22  更:2021-08-01 14:20:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/1 10:37:52-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码