☰
C++常见通用自写算法集合
2026/10/22 12:51:08 网站建设 项目流程

目录

1.字符串大小写转换

2.容器内容去重

3.求文件的sha值

3.1.在vcpkg中安装openssl和asio库

3.2.同步读取文件求sha值

3.3.优化:用内存映射方式求sha值

3.4.优化:异步读取文件求sha值

3.5.几种方法性能比较

4.求容器的最大最小值

5.求容器内元素的平均值


1.字符串大小写转换

CommFunc.h

#pragma once #include <string> namespace util { std::string to_upper(std::string_view src); std::string to_upper(const std::string& src); std::string to_upper(std::string& src); std::string to_lower(std::string_view src); std::string to_lower(const std::string& src); std::string to_lower(std::string& src); } // namespace util

CommFunc.cpp

#include "CommFunc.h" #include <algorithm> #include <cctype> #include <iterator> namespace util { template<typename Container, typename Func = int(int)> std::string _transform(Container&& src, Func func) { std::string dst; dst.reserve(src.size()); std::transform(src.cbegin(), src.cend(), std::back_inserter(dst), [toUpper = std::move(func)](unsigned char c) -> char { return static_cast<char>(toUpper(static_cast<int>(c))); }); return dst; } std::string to_upper(std::string_view src) { #if 0 std::string dst; dst.reserve(src.size()); std::transform(src.cbegin(), src.cend(), std::back_inserter(dst), [](unsigned char c) -> char { return static_cast<char>(std::toupper(static_cast<int>(c))); }); return dst; #else return _transform(src, std::toupper); #endif } std::string to_upper(const std::string& src) { #if 0 std::string dst; dst.reserve(src.size()); std::transform(src.cbegin(), src.cend(), std::back_inserter(dst), [](unsigned char c) -> char { return static_cast<char>(std::toupper(static_cast<int>(c))); }); return dst; #else return _transform(src, std::toupper); #endif } std::string to_upper(std::string& src) { std::transform(src.begin(), src.end(), src.begin(), [](unsigned char c) -> char { return static_cast<char>(std::toupper(static_cast<int>(c))); }); return src; } std::string to_lower(std::string_view src) { #if 0 std::string dst; dst.reserve(src.size()); std::transform(src.cbegin(), src.cend(), std::back_inserter(dst), [](unsigned char c) -> char { return static_cast<char>(std::tolower(static_cast<int>(c))); }); return dst; #else return _transform(src, std::tolower); #endif } std::string to_lower(const std::string& src) { #if 0 std::string dst; dst.reserve(src.size()); std::transform(src.cbegin(), src.cend(), std::back_inserter(dst), [](unsigned char c) -> char { return static_cast<char>(std::tolower(static_cast<int>(c))); }); return dst; #else return _transform(src, std::tolower); #endif } std::string to_lower(std::string& src) { std::transform(src.begin(), src.end(), src.begin(), [](unsigned char c) -> char { return static_cast<char>(std::tolower(static_cast<int>(c))); }); return src; } } // namespace util

优点:不需要手动 resize,不会因为大小不匹配出现内存 bug,推荐。

如果还是报错:改用全局 ::tolower(部分旧编译器 / 库的坑)

std::transform(src.cbegin(), src.cend(), std::back_inserter(dst), [](unsigned char c) -> char { return static_cast<char>(::tolower(static_cast<int>(c))); });

区别:std::tolower是 C++ 版本,::tolower是 C 标准库全局符号;有些平台头文件混乱会找不到std::tolower。

2.容器内容去重

C++反射之检测struct或class是否实现指定函数

//容器内容去重 template<typename Container, typename Compare = std::less<>> void deduplicateContainer(Container& container, Compare comp = Compare());

详细实现:

#pragma once #include <algorithm> #include <iterator> #include <type_traits> namespace util { #define HAS_NON_STATIC_AND_NON_CONST_MEMBER_EX1(R, member) \ template<typename T> struct has_non_static_member_ex1_##member{ \ private:\ template<typename Y, Y y> \ class Helper; \ template<typename U = T> \ constexpr static bool has##member(...) {\ return false;\ }\ template<typename U = T>\ constexpr static bool has##member(Helper<R (U::*)(), &U::member>*) {\ return true;\ }\ public:\ static const bool value = has##member<T>(nullptr);\ }; HAS_NON_STATIC_AND_NON_CONST_MEMBER_EX1(void, sort) // 判断容器是否可以使用 std::sort template<typename Container> constexpr bool can_use_std_sort = std::is_base_of_v< std::random_access_iterator_tag, typename std::iterator_traits<typename Container::iterator>::iterator_category >; //容器内容去重 template<typename Container, typename Compare> void deduplicateContainer(Container& container, Compare comp) { if constexpr (can_use_std_sort<Container>) { std::sort(container.begin(), container.end(), comp); auto it = std::unique(container.begin(), container.end()); container.erase(it, container.end()); } else if constexpr (has_non_static_member_ex1_sort<Container>::value){ container.sort(comp); auto it = std::unique(container.begin(), container.end()); container.erase(it, container.end()); } else { static_assert(false, "Container not support sort!!!"); } } } // namespace util

3.求文件的sha值

3.1.在vcpkg中安装openssl和asio库

//安装asio和openssl vcpkg install asio openssl:x64-windows //在cmake引用 cmake_minimum_required(VERSION 3.16) project(file_hash) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(asio CONFIG REQUIRED) find_package(OpenSSL REQUIRED) add_executable(file_hash main.cpp) target_link_libraries(file_hash PRIVATE asio::asio OpenSSL::Crypto)

3.2.同步读取文件求sha值

std::string calc_sha256_sync(const std::string& path) { std::ifstream f(path, std::ios::binary); if (!f) return ""; SHA256_CTX ctx; SHA256_Init(&ctx); const size_t BUF_SIZE = 2*1024 * 1024; std::vector<char> buf(BUF_SIZE); while (true) { f.read(buf.data(), buf.size()); auto read_len = f.gcount(); if (read_len > 0) { SHA256_Update(&ctx, buf.data(), read_len); } if (!f) break; } unsigned char h[SHA256_DIGEST_LENGTH]; SHA256_Final(h, &ctx); char hexbuf[SHA256_DIGEST_LENGTH * 2 + 1]{}; for (int i = 0;i < SHA256_DIGEST_LENGTH;i++) sprintf(hexbuf + i * 2, "%02x", h[i]); return std::string(hexbuf); }

3.3.优化:用内存映射方式求sha值

#pragma once #include <windows.h> #include <openssl/evp.h> #include <string> #include <iostream> namespace { constexpr char hex_lut2[] = "0123456789abcdef"; } // Windows原生内存映射计算SHA256 std::string calc_sha256_mmap_win(const std::string& file_path) { HANDLE hFile = CreateFileA( file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, // 顺序访问提示,内核预读优化 nullptr ); if (hFile == INVALID_HANDLE_VALUE) { std::cerr << "CreateFile failed\n"; return {}; } LARGE_INTEGER file_size{}; if (!GetFileSizeEx(hFile, &file_size)) { CloseHandle(hFile); return {}; } HANDLE hMap = CreateFileMappingA(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr); if (hMap == nullptr) { CloseHandle(hFile); return {}; } // 映射整个文件到内存 const char* p_data = static_cast<const char*>(MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)); if (p_data == nullptr) { CloseHandle(hMap); CloseHandle(hFile); return {}; } // 哈希计算 EVP_MD_CTX* md_ctx = EVP_MD_CTX_new(); EVP_DigestInit_ex(md_ctx, EVP_sha256(), nullptr); EVP_DigestUpdate(md_ctx, p_data, static_cast<size_t>(file_size.QuadPart)); unsigned char hash_bin[EVP_MAX_MD_SIZE]; unsigned int hash_len = 0; EVP_DigestFinal_ex(md_ctx, hash_bin, &hash_len); EVP_MD_CTX_free(md_ctx); // 转hex字符串 std::string hash_str; hash_str.resize(hash_len * 2); for (unsigned int i = 0; i < hash_len; ++i) { hash_str[i * 2] = hex_lut[hash_bin[i] >> 4]; hash_str[i * 2 + 1] = hex_lut[hash_bin[i] & 0x0F]; } // 资源释放,必须成对 UnmapViewOfFile(p_data); CloseHandle(hMap); CloseHandle(hFile); return hash_str; }

3.4.优化:异步读取文件求sha值

#include <asio.hpp> #include <iostream> #include <vector> #include <functional> #include <fstream> #include <openssl/sha.h> namespace { // 编译期十六进制查表,比 sprintf 快 3~5 倍 constexpr char hex_lut[] = "0123456789abcdef"; // 缓冲区大小:2MB 大缓冲减少系统调用次数,可按场景调整为 256KB/1MB constexpr size_t BUF_SIZE = 2 * 1024 * 1024; } std::string asyn_read_file_optimized(const std::string& path) { try { asio::io_context io_context; // 优化1:改用 stream_file 顺序流读取 // 比 random_access_file 更适合连续读取:系统自动预读、无需维护偏移、调度开销更低 asio::stream_file file(io_context, path, asio::stream_file::read_only); SHA256_CTX sha_ctx; SHA256_Init(&sha_ctx); std::string hash_result; // 优化2:双缓冲流水线(核心性能优化) // 重叠「磁盘IO」和「哈希计算」,总耗时 = max(IO耗时, 计算耗时),而非两者相加 std::vector<char> buf_a(BUF_SIZE); std::vector<char> buf_b(BUF_SIZE); bool use_buf_a = true; // 标记当前用于IO的缓冲区 std::function<void()> read_loop; read_loop = [&]() { // 选择当前IO缓冲区 char* io_buf = use_buf_a ? buf_a.data() : buf_b.data(); size_t io_buf_len = use_buf_a ? buf_a.size() : buf_b.size(); file.async_read_some(asio::buffer(io_buf, io_buf_len), [&, read_loop, io_buf](const asio::error_code& ec, size_t bytes_read) { if (ec) { if (ec == asio::error::eof) { // 读取结束,生成最终哈希 unsigned char hash_bin[SHA256_DIGEST_LENGTH]; SHA256_Final(hash_bin, &sha_ctx); //优化4:查表法十六进制转换,零格式解析开销 hash_result.resize(SHA256_DIGEST_LENGTH * 2); for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) { hash_result[i * 2] = hex_lut[hash_bin[i] >> 4]; hash_result[i * 2 + 1] = hex_lut[hash_bin[i] & 0x0F]; } } else { std::cerr << "read error: " << ec.message() << '\n'; } return; } // 流水线核心:先切换缓冲区、发起下一次IO(让磁盘先跑) use_buf_a = !use_buf_a; read_loop(); // 异步发起下一块读取,内核后台执行,立即返回 // 同时计算当前块哈希,与「下一块的IO」完全并行 if (bytes_read > 0) { SHA256_Update(&sha_ctx, io_buf, bytes_read); } }); }; read_loop(); io_context.run(); return hash_result; } catch (const std::exception& e) { std::cerr << "exception: " << e.what() << '\n'; return {}; } }

双缓冲流水线,大缓冲区(2MB)减少系统调用次数,降低内核态切换开销。

  • 原理:原代码是串行流程:发起IO → 等待完成 → 计算哈希 → 发起下一次IO,IO 和计算完全串行。
  • 优化后:当前块读完后,立刻发起下一块的异步读,再回头计算当前块的哈希。磁盘 IO 和 CPU 计算完全重叠,总耗时从「IO 总时间 + 计算总时间」缩短为max(IO总时间, 计算总时间)。
  • 收益:大文件场景下,SSD 环境提升 30%~50%,高速存储环境下最高提升近 1 倍。

3.5.几种方法性能比较

文件 Hash 计算工具(Windows,用来校验你前面 C++ 代码的 SHA256 结果)

Windows 系统自带(无需安装,优先用,和你的 C++ 代码做比对)

certutil(CMD / PowerShell 都支持)

# SHA256(和你代码一致!推荐) certutil -hashfile "D:\test.dat" SHA256 # MD5 certutil -hashfile "D:\test.dat" MD5 # SHA1 certutil -hashfile "D:\test.dat" SHA1

支持:MD2 MD4 MD5 SHA1 SHA256 SHA384 SHA512

测试结果:

4.求容器的最大最小值

/// 通用:求容器最大值 template<typename Container> auto container_max(const Container& c) { if (c.empty()) throw std::runtime_error("container is empty!"); return *std::max_element(c.cbegin(), c.cend()); } /// 通用:求容器最小值 template<typename Container> auto container_min(const Container& c) { if (c.empty()) throw std::runtime_error("container is empty!"); return *std::min_element(c.cbegin(), c.cend()); }

5.求容器内元素的平均值

// 通用模板:支持任意可迭代序列 + 算术类型元素约束 template<typename Container> auto average(Container&& cont) -> std::enable_if_t< std::is_arithmetic_v< typename std::iterator_traits<decltype(std::begin(cont))>::value_type>, double > { // 提取元素真实类型 using ValueType = typename std::iterator_traits<decltype(std::begin(cont))>::value_type; // 空容器安全处理,避免除以0 const auto size = std::size(cont); if (size == 0) { return 0.0; } // 累加所有元素的绝对值,初始值0.0保证浮点精度 double sumAbs = std::accumulate( std::cbegin(cont), std::cend(cont), 0.0, [](double accum, ValueType val) { return accum + val; } ); return sumAbs / static_cast<double>(size); } // 新增重载:专门支持花括号初始化列表 {a,b,c,...} template<typename T> std::enable_if_t<std::is_arithmetic_v<T>, double> average(std::initializer_list<T> list) { // 直接复用通用版本的计算逻辑,代码不重复 return average(list); }

调用示例:

std::vector<int> vecInts = {1, 2, 100, 333, 444, -1}; auto sumOfInt = average(vecInts); std::vector<double> vecDoubles = { 1, 2, 100, 333, 444, 2}; auto sumOfDouble = average(vecDoubles); int a[] = {11, 33,23333, 435, 2211212, 3333, 5555,6666,432,111,233,3,3,33,34 }; auto sumOfInt1 = average(a); // 3. std::array 固定数组 std::array<int, 4> arrayValue = { -10, 20, -30, 40 }; std::cout << "array均值: " << average(arrayValue) << "\n"; auto sumOfInt2 = average({11,22,33,44,56,777,88,99});

后续待完。。。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询