24、C++ 流处理、包装器及字符串与数字转换

C++ 流处理、包装器及字符串与数字转换

1. 流包装器的使用

1.1 流参数与包装器概念

std::ios_base 提供的参数(如 left right hex width precision 等)是一个封闭集合,自 20 世纪 80 年代中期定义后基本未变。由于每个操纵器都会修改流状态中的一个参数,所以操纵器集合本质上也是封闭的。现代影响特定数据值格式的方法是使用包装器。

1.2 自定义包装器示例

以下是一个自定义包装器的示例,用于在数据文件中引用值:

template<class InputIt, class OutputIt>
OutputIt do_quote(InputIt begin, InputIt end, OutputIt dest)
{
    *dest++ = '"';
    while (begin != end) {
        auto ch = *begin++;
        if (ch == '"') {
            *dest++ = '\\';
        }
        *dest++ = ch;
    }
    *dest++ = '"';
    return dest;
}

struct quoted {
    std::string_view m_view;
    quoted(const char *s) : m_view(s) {}
    quoted(const std::string& s) : m_view(s) {}
};

std::ostream& operator<< (std::ostream& os, const quoted& q)
{
    do_quote(
        q.m_view.begin(),
        q.m_view.end(),
        std::ostreambuf_iterator<char>(os)
    );
    return os;
}

使用这个包装器,我们可以写出如下代码:

std::cout << quoted("I said \"hello\".");

1.3 与标准库包装器的比较

自定义包装器与标准库 <iomanip> 头文件中的 std::quoted 包装器函数类似,但 std::quoted 不使用基于迭代器的算法生成输出,而是在本地 std::string 变量中构造整个输出,然后一次性打印。这意味着 std::quoted 不支持分配器,不适合禁止堆分配的环境。

1.4 推荐使用包装器

建议使用描述自包含格式化操作的包装器,而不是操纵器,因为操纵器会“粘性”地改变流的状态。例如,错误放置的 std::hex 可能会影响后续输出。

2. 解决粘性操纵器问题

2.1 保存和恢复流状态

可以通过在每个复杂输出操作前后保存和恢复 ostream 的状态来解决“粘性 std::hex ”问题,示例代码如下:

void test() {
    std::ios old_state(nullptr);
    old_state.copyfmt(std::cout);
    std::cout << std::hex << 225; // "e1"
    std::cout.copyfmt(old_state);
    std::cout << 42; // "42"
}

2.2 创建新的 ostream

也可以每次输出时创建一个全新的 ostream ,示例代码如下:

void test() {
    std::ostream os(std::cout.rdbuf());
    os << std::hex << 225; // "e1"
    std::cout << 42; // "42"
}

2.3 iostreams 格式化的缺点

iostreams 格式化存在一些缺点,例如每个消息片段在遇到相应的 operator<< 时会立即输出,并且在国际化方面表现不佳,因为源代码中没有整体消息的“形状”,不利于翻译。因此,不建议将 iostreams 作为代码库的基础,建议使用 <stdio.h> 或第三方库(如 fmt )进行格式化。

3. 使用 ostringstream 进行格式化

3.1 ostringstream 简介

ostringstream 类似于 ostream ,提供所有常见的 operator<< 功能,但它由 stringbuf 支持,将数据写入可调整大小的字符缓冲区(实际上是 std::string )。可以使用 oss.str() 方法获取这个字符串的副本。

3.2 字符串化对象的示例

以下是将任何类型 T 的对象“字符串化”的示例:

template<class T>
std::string to_string(T&& t)
{
    std::ostringstream oss;
    oss << std::forward<T>(t);
    return oss.str();
}

在 C++17 中,还可以考虑多参数版本的 to_string

template<class... Ts>
std::string to_string(Ts&&... ts)
{
    std::ostringstream oss;
    (oss << ... << std::forward<Ts>(ts));
    return oss.str();
}

4. 本地化问题

4.1 本地化概念

使用 printf ostream 进行字符串格式化(或字符串解析)时,需要注意本地化问题。本地化是用户环境中依赖于语言和文化习俗的子集,通过操作系统以编程方式暴露,允许程序根据用户的首选本地化调整行为。

4.2 本地化问题示例

以下示例展示了本地化对字符串格式化的影响:

std::setlocale(LC_ALL, "C.UTF-8");
std::locale::global(std::locale("C.UTF-8"));
auto json = to_string('[', 3.14, ']');
assert(json == "[3.14]"); // Success!
std::setlocale(LC_ALL, "en_DK.UTF-8");
std::locale::global(std::locale("en_DK.UTF-8"));
json = to_string('[', 3.14, ']');
assert(json == "[3,14]"); // Silent, abject failure!

4.3 本地化的性能成本

“当前本地化”是一个全局变量,每次访问都需要原子访问或全局互斥锁保护。每次调用 snprintf operator<<(ostream&, double) 都必须访问当前本地化,这会带来巨大的性能成本,在多线程代码中可能成为性能瓶颈。

4.4 应对本地化问题的建议

  • 应用程序程序员 :对于一定复杂度的应用程序,应在 main() 函数的第一行设置 std::locale::global(std::locale("C")) 。如果不信任用户使用 UTF - 8,可以考虑使用 "C.UTF-8" ,但要注意该名称自 2015 年左右才出现,旧系统可能不支持。
  • 第三方库程序员 :有两种选择。较简单的方法是假设库仅在全局本地化设置为 "C" 的应用程序中使用,可随意使用 snprintf operator<< ;较难的方法是避免使用所有依赖本地化的格式化函数,这在 C++17 中借助一些新的库设施才变得可行。

5. 数字转换为字符串

5.1 整数转换为字符串

C++17 提供了以下几种将整数转换为字符串的方法:
| 方法 | 头文件 | 本地化问题 | 内存分配 | 支持进制 | 优点 | 缺点 |
| ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| snprintf | <stdio.h> | 无( %d 不受本地化影响) | 非分配 | 8、10、16 | 本地化独立,非分配 | 仅支持 8、10、16 进制 |
| oss << intvalue | <sstream> | 有(可能插入千位分隔符) | 分配,支持分配器 | 8、10、16 | 支持多种进制 | 存在本地化问题,需要分配内存 |
| std::to_string | <string> | 无(等同于 %d ) | 分配,不支持分配器 | 10 | 方便组合成大消息 | 仅支持十进制,不支持分配器 |
| std::to_chars | <charconv> | 无 | 非分配 | 2 - 36 | 本地化独立,非分配,支持多种进制 | 是 C++17 新特性,标准库主要实现中可能未包含 |

5.2 浮点数转换为字符串

C++17 提供了以下几种将浮点数转换为字符串的方法:
| 方法 | 头文件 | 本地化问题 | 内存分配 | 格式化调整 | 优点 | 缺点 |
| ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| snprintf | <stdio.h> | 有(小数点问题) | 非分配 | 可调整 | 非分配 | 存在本地化问题 |
| oss << floatvalue | <sstream> | 有(小数点问题) | 分配,支持分配器 | 可调整 | 支持分配器,可调整格式化 | 存在本地化问题,需要分配内存 |
| std::to_string | <string> | 有(等同于 %f ) | 分配,不支持分配器 | 不可调整 | 方便使用 | 存在本地化问题,不可调整格式化,不支持分配器 |
| std::to_chars | <charconv> | 无 | 非分配 | 可调整 | 本地化独立,非分配,可调整格式化 | 是 C++17 新特性,标准库主要实现中可能未包含 |

以下是整数转换的 mermaid 流程图:

graph TD;
    A[整数转换为字符串] --> B[snprintf];
    A --> C[oss << intvalue];
    A --> D[std::to_string];
    A --> E[std::to_chars];

6. 字符串转换为数字

6.1 字符串转换为整数

C++17 提供了以下几种将字符串转换为整数的方法:
| 方法 | 头文件 | 溢出处理 | 错误处理 | 本地化问题 | 内存分配 | 支持进制 | 优点 | 缺点 |
| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| strtol | <stdlib.h> | 饱和处理 | 设置全局 errno | 理论上有 | 非分配 | 0、2 - 36 | 支持多种进制,跳过前导空格 | 溢出通过全局变量通信,不适合线程或高性能代码 |
| sscanf | <stdio.h> | 无法检测 | 返回 0 | 有 | 非分配 | 0、8、10、16 | 简单易用 | 无法检测溢出 |
| std::stoi | <string> | 抛出异常 | 抛出异常 | 有 | 非分配 | 0、2 - 36 | 错误检测好 | 仅适用于 std::string 类型 |
| iss >> intvalue | <sstream> | 饱和处理 | 设置 iss.fail() | 有 | 分配,支持分配器 | 8、10、16 | 支持分配器 | 存在本地化问题,需要分配内存 |
| std::from_chars | <charconv> | 设置 r.ec != 0 | 设置 r.ec != 0 | 无 | 非分配 | 2 - 36 | 现代且高性能,输入缓冲区无需空终止 | 不能禁止跳过空格,仅支持小写十六进制输入 |

6.2 字符串转换为浮点数

C++17 提供了以下几种将字符串转换为浮点数的方法:
| 方法 | 头文件 | 溢出处理 | 错误处理 | 本地化问题 | 内存分配 | 支持进制 | 优点 | 缺点 |
| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| strtof | <stdlib.h> | 饱和处理 | 设置全局 errno | 有 | 非分配 | 10 或 16(自动检测) | 非分配 | 溢出通过全局变量通信,不适合线程或高性能代码 |
| sscanf | <stdio.h> | 无法检测 | 返回 0 | 有 | 非分配 | 10 或 16(自动检测) | 简单易用 | 无法检测溢出 |
| std::stof | <string> | 抛出异常 | 抛出异常 | 有 | 非分配 | 10 或 16(自动检测) | 错误检测好 | 仅适用于 std::string 类型 |
| iss >> floatvalue | <sstream> | 饱和处理 | 设置 iss.fail() | 有 | 分配,支持分配器 | 10 或 16(自动检测) | 支持分配器 | 存在本地化问题,需要分配内存 |
| std::from_chars | <charconv> | 设置 r.ec != 0 | 设置 r.ec != 0 | 无 | 非分配 | 10 或 16(自动检测) | 本地化独立,非分配 | 不能禁止跳过空格 |

6.3 解析方法的选择建议

  • 避免使用 atoi ,因为其在无效输入时行为未定义。
  • std::stoi 适合一次性解析用户输入,但不适合高性能工作,且仅适用于 std::string 类型。
  • std::from_chars 是解析整数最现代和高性能的选择,但有一些限制,如不能禁止跳过空格,仅支持小写十六进制输入。

6.4 输入解析的建议

建议将输入的验证(或词法分析)与解析分开。如果能预先验证字符串只包含数字或符合有效浮点数的正则表达式语法,只需选择能检测溢出和/或尾随文本的解析方法,如 std::stof std::from_chars

以下是字符串转换为整数的 mermaid 流程图:

graph TD;
    A[字符串转换为整数] --> B[strtol];
    A --> C[sscanf];
    A --> D[std::stoi];
    A --> E[iss >> intvalue];
    A --> F[std::from_chars];

7. 总结与最佳实践

7.1 流处理与包装器总结

  • 流包装器是现代 C++ 中调整数据值格式的有效方式,相较于传统的操纵器,它能避免流状态的“粘性”改变,使代码更具可读性和可维护性。例如自定义的 quoted 包装器和标准库中的 std::quoted 都展示了包装器的使用方法。
  • 解决粘性操纵器问题可以通过保存和恢复流状态或创建新的 ostream 来实现,但 iostreams 格式化存在输出即时性和国际化方面的缺点,因此建议使用 <stdio.h> 或第三方库进行格式化。

7.2 本地化问题总结

  • 本地化问题在字符串格式化和解析中是一个重要的考虑因素,它不仅会影响输出结果的正确性,还会带来性能成本。
  • 应用程序程序员和第三方库程序员应根据自身情况采取不同的策略来应对本地化问题,如设置全局本地化或避免使用依赖本地化的函数。

7.3 数字与字符串转换总结

  • 在数字转换为字符串和字符串转换为数字的过程中,C++17 提供了多种方法,每种方法都有其优缺点。例如, std::to_chars std::from_chars 是比较现代和高性能的选择,但它们也有一些限制。
  • 在选择转换方法时,需要考虑本地化问题、内存分配、支持的进制、错误处理等因素。

7.4 最佳实践建议

  • 使用包装器 :优先使用描述自包含格式化操作的包装器,避免使用粘性操纵器。
  • 处理本地化 :在代码中合理处理本地化问题,根据应用场景选择合适的本地化设置。
  • 选择转换方法 :根据具体需求选择数字与字符串转换的方法,如对于高性能场景,优先考虑 std::to_chars std::from_chars ;对于一次性解析用户输入,可使用 std::stoi std::stof
  • 分离验证与解析 :将输入的验证(或词法分析)与解析分开,提高解析的准确性和效率。

7.5 整体流程总结

以下是一个整体的流程图,展示了从数字转换为字符串、字符串转换为数字以及处理本地化问题的流程:

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px

    A(数字):::process -->|转换为字符串| B{选择方法}:::process
    B -->|snprintf| C(snprintf 转换):::process
    B -->|oss << intvalue| D(oss 转换):::process
    B -->|std::to_string| E(std::to_string 转换):::process
    B -->|std::to_chars| F(std::to_chars 转换):::process

    G(字符串):::process -->|转换为数字| H{选择方法}:::process
    H -->|strtol| I(strtol 解析):::process
    H -->|sscanf| J(sscanf 解析):::process
    H -->|std::stoi| K(std::stoi 解析):::process
    H -->|iss >> intvalue| L(iss 解析):::process
    H -->|std::from_chars| M(std::from_chars 解析):::process

    N(本地化设置):::process -->|影响转换| B
    N -->|影响解析| H

7.6 代码示例汇总

以下是本文中涉及的主要代码示例汇总:

自定义包装器示例
template<class InputIt, class OutputIt>
OutputIt do_quote(InputIt begin, InputIt end, OutputIt dest)
{
    *dest++ = '"';
    while (begin != end) {
        auto ch = *begin++;
        if (ch == '"') {
            *dest++ = '\\';
        }
        *dest++ = ch;
    }
    *dest++ = '"';
    return dest;
}

struct quoted {
    std::string_view m_view;
    quoted(const char *s) : m_view(s) {}
    quoted(const std::string& s) : m_view(s) {}
};

std::ostream& operator<< (std::ostream& os, const quoted& q)
{
    do_quote(
        q.m_view.begin(),
        q.m_view.end(),
        std::ostreambuf_iterator<char>(os)
    );
    return os;
}
保存和恢复流状态示例
void test() {
    std::ios old_state(nullptr);
    old_state.copyfmt(std::cout);
    std::cout << std::hex << 225; // "e1"
    std::cout.copyfmt(old_state);
    std::cout << 42; // "42"
}
创建新的 ostream 示例
void test() {
    std::ostream os(std::cout.rdbuf());
    os << std::hex << 225; // "e1"
    std::cout << 42; // "42"
}
字符串化对象示例
template<class T>
std::string to_string(T&& t)
{
    std::ostringstream oss;
    oss << std::forward<T>(t);
    return oss.str();
}

template<class... Ts>
std::string to_string(Ts&&... ts)
{
    std::ostringstream oss;
    (oss << ... << std::forward<Ts>(ts));
    return oss.str();
}
本地化问题示例
std::setlocale(LC_ALL, "C.UTF-8");
std::locale::global(std::locale("C.UTF-8"));
auto json = to_string('[', 3.14, ']');
assert(json == "[3.14]"); // Success!
std::setlocale(LC_ALL, "en_DK.UTF-8");
std::locale::global(std::locale("en_DK.UTF-8"));
json = to_string('[', 3.14, ']');
assert(json == "[3,14]"); // Silent, abject failure!

通过以上内容,我们对 C++ 中的流处理、包装器、本地化问题以及数字与字符串转换有了更深入的了解。在实际编程中,我们可以根据具体需求选择合适的方法和策略,以提高代码的质量和性能。

【提高晶格缩减(LR)辅助预编码中VP的性能】向量扰动(VP)预编码在下行链路中多用户通信系统中的应用(Matlab代码实现)内容概要:本文主要介绍了一项关于提高晶格缩减(LR)辅助预编码中向量扰动(VP)预编码性能的研究,重点探讨VP预编码在下行链路多用户通信系统中的应用,并提供了基于Matlab的代码实现。该技术旨在优化多用户MIMO系统中的信号预处理,通过晶格缩减提升VP预编码的性能,从而改善系统吞吐量误码率表现。文中还列举了大量相关科研方向技术服务内容,涵盖智能优化算法、机器学习、信号处理、电力系统、路径规划等多个领域,展示了广泛的技术应用场景研究支持能力。; 适合人群:具备通信工程、电子信息、自动化或相关专业背景的研究生、科研人员及工程技术人员,熟悉Matlab编程并有一定无线通信系统理论基础者更佳。; 使用场景及目标:①研究多用户MIMO系统中的预编码技术优化方案;②提升VP预编码在实际通信系统中的性能表现;③结合Matlab仿真验证晶格缩减向量扰动技术的有效性;④拓展至其他通信优化问题的研究复现。; 阅读建议:建议读者结合文中提供的Matlab代码进行仿真实践,重点关注VP预编码晶格缩减的结合机制,同时可参考文档中列出的其他研究案例进行横向对比技术迁移,以深化对通信系统优化方法的理解应用。
本研究聚焦于运用Simscape Electrical这一MATLAB环境中的专业工具集,构建并仿真双区域中压直流船舶电力网络。该工具集支持工程师通过图形化界面完成复杂电气架构的设计性能分析,无需直接处理底层数学建模过程。项目文件中包含的“IdealRectifier”“ThyristorRectifier”模块分别对应无损耗理想整流单元及具备开关特性的晶闸管整流装置,二者在系统中承担交流至直流电能转换的核心功能。理想整流器忽略实际损耗,而晶闸管型则更贴近工程实践中的器件行为。 项目文档中,“SECURITY.md”“LICENSE.md”为常规开源协议文件,阐明项目安全规范及使用授权条款;“resources”目录存储辅助建模所需的图像、数据集等资源;“Tutorial”提供逐步操作指引,协助新用户掌握系统仿真流程;“initParams.mat”文件预存系统运行所需的电气参数,包括电压基准、负载条件及能效指标;“Two_Zone_MVDC_Electric_Ship.pdf”为技术文档,系统阐述船舶直流电力体系的设计理论建模方法论;“Two_Zone_MVDC.prj”作为完整工程文件,集成全部模型配置结构设定;“README.md”则概述项目目标基础操作规范。 通过本案例,研究者可掌握基于Simscape Electrical的电力系统建模技术,涵盖整流单元选型、参数配置、动态仿真及结果解析全流程。该案例为船舶电力推进、能源管理及工业自动化领域的学术研究工程应用提供了典型参考范例。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
标题踏雪阁民宿订购平台优化发展研究AI更换标题第1章引言介绍踏雪阁民宿订购平台的研究背景、意义、国内外民宿订购平台研究现状及论文创新点。1.1研究背景意义阐述民宿订购平台的发展趋势及踏雪阁平台的研究价值。1.2国内外研究现状分析国内外民宿订购平台的发展现状存在的问题。1.3研究方法及创新点概述本文的研究方法,并指出研究的创新之处。第2章相关理论总结民宿订购平台相关理论,确立研究理论基础。2.1电子商务平台理论介绍电子商务平台的基本概念、特点及运营模式。2.2民宿行业特点需求分析民宿行业的特点及用户需求,为平台设计提供依据。2.3用户体验设计理论阐述用户体验设计原则和方法,提升平台用户满意度。第3章踏雪阁民宿订购平台设计详细介绍踏雪阁民宿订购平台的设计方案和实现过程。3.1平台架构设计给出平台的整体架构,包括前端、后端及数据库设计。3.2功能模块设计详细介绍平台的各个功能模块,如民宿展示、在线预订、支付结算等。3.3用户界面设计阐述用户界面的设计原则和实现方法,提升用户体验。第4章数据收集分析方法介绍数据收集和分析的方法,为平台优化提供依据。4.1数据收集方法阐述数据收集的途径和工具,如用户调研、日志分析等。4.2数据分析方法介绍数据分析的方法和技术,如数据挖掘、统计分析等。4.3数据可视化呈现通过图表等形式展示数据分析结果,便于理解和决策。第5章平台优化策略实施效果提出平台优化策略,并分析实施效果。5.1优化策略制定根据数据分析结果,制定针对性的优化策略。5.2优化实施过程详细介绍优化策略的实施过程,包括技术实现和推广策略。5.3实施效果评估通过数据对比和用户反馈,评估优化策略的实施效果。第6章结论展望总结本文的研究成果,并展望未来的研究方向。6.1研究结论概括本文的主要研究结论,包括平台设计、优化策略及实施效果。6.2展望指出踏雪阁民宿订购平台未来的发展方向
【概率Copula分类器】实现d维阿基米德Copula相关的函数、HACs相关的函数研究(Matlab代码实现)内容概要:本文主要围绕“概率Copula分类器”的研究展开,重点介绍了d维阿基米德Copula相关函数及层次阿基米德Copula(HACs)函数的Matlab代码实现方法。通过对Copula理论的应用,实现对多变量数据间复杂依赖结构的建模,尤其适用于金融、能源等领域中存在非线性相关性和尾部依赖的风险分析预测任务。文中结合具体Matlab编程实例,展示了Copula函数在场景生成、概率预测和统计推断中的实际应用,体现了其在处理高维数据相关性方面的优势。; 适合人群:具备一定统计学基础和Matlab编程能力的研究生、科研人员及从事数据分析、风险管理、电力系统或金融工程等相关领域的技术人员;熟悉概率论数理统计、对高维数据建模有兴趣的研究者更为适宜。; 使用场景及目标:①利用Copula函数构建风光发电出力间的相关性模型,用于新能源功率预测场景生成;②在多变量风险评估中捕捉变量之间的尾部依赖关系,提升预测精度;③通过HACs实现层次化依赖结构建模,增强分类器对复杂数据结构的适应能力;④为学术研究提供可复现的Matlab代码框架,推动Copula理论在实际工程中的应用。; 阅读建议:建议读者结合文中提供的Matlab代码逐段理解算法实现流程,重点关注边缘分布拟合、参数估计随机抽样等关键步骤。同时推荐参考文档中列出的相关案例(如风电预测、微电网优化)进行拓展学习,以加深对Copula模型应用场景的理解,并鼓励在实际项目中尝试改进集成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值