【问题标题】:How to format {} in a json string using fmt?如何使用 fmt 在 json 字符串中格式化 {}?
【发布时间】:2021-07-28 12:40:42
【问题描述】:

我正在尝试格式化 JSON 字符串以避免使用 fmt::format,因为 json 确实包含 {0} 并且 fmt 无法区分 JSON 的括号和格式说明符 {0} 的括号。如果使用 fmt::format 我得到以下错误:

terminate called after throwing an instance of 'fmt::v7::format_error'
  what():  invalid format string
Aborted (core dumped)

这就是我尝试使用fmt::sprintf 的原因。我找不到对 c++ 有用的解决方案,但我看到了一个 Golang 解决方案,但它不起作用,即:fmt.Sprintf("%[2]d %[1]d\n", 11, 22)(当然我已将其更改为 fmt::sprintf)。

如何使用 fmt::sprintf 为格式说明符提供参数索引?

提前致谢。

编辑:

JSON 不是我生成的,而是 sqlite 生成的。该 json 包含一个 {0},由于外部 JSON 大括号,它没有被格式化。

【问题讨论】:

  • because i cannot escape all 为什么不呢? I need the same thing but with fmt::sprintf() 我非常怀疑你需要那个,无论如何,here's the docsher's posix docs 而你正在搜索 %1d
  • @KamilCuk 仔细阅读问题(包括错误信息),你就会明白为什么了。
  • 我不明白,它如何改变任何东西?所以逃离他们。 *您正在搜索%1$d
  • @KamilCuk: fmt 使用{{ 来转义大括号,而不是\{...
  • 该问题与 fmt::format 或 fmt::sprintf 无关,而是与使用相同令牌的 JSON 和 FMT 有关。我认为你不能通过使用 fmt::sprintf 轻松解决这个问题。 JSON 解析器会尝试解析 {0},就像 FMT 会尝试解析 {"iq"....}

标签: c++ json fmt


【解决方案1】:

说实话,你的问题不是很清楚,但我几乎可以肯定这是一个典型的 XY 问题示例。

恕我直言,主要错误是您尝试手动格式化 JSON,根据经验,这非常棘手且容易出错。

除非您实际上是在一组非常小且严格的输入上进行操作,否则几乎 100% 确定您会在某些情况下生成格式错误的 JSON 输出。例如,您还必须记住所有字符串都应该是有效的 UTF-8,这意味着您必须检查输入或转义它们可能包含的任何奇怪字符。您还必须转义 JSON 字符串中的任何无效内容,例如 "like specified by the JSON standard

这就是为什么我认为手动生成 JSON 只能作为最后的解决方案,即如果您有嵌入式设备并且由于极其严重的内存限制而无法使用任何类型的库。

我经常推荐的一个库是nlohmann/json,它可能不是1 附近最快的,但它绝对是最容易使用的库之一。这主要是因为它是纯标头库以及 nlohmann::json 与 STL 算法和迭代器兼容的事实(它的行为基本上类似于 std::map 或向量)。

然后你可以做一些非常整洁的事情,例如:

#include <iostream>
#include <string_view>
#include <unordered_set>

#include <nlohmann/json.hpp>

int main() {
    const std::unordered_set<std::string_view> s { "a", "b", "c" };

    nlohmann::json jarr {};

    for (const auto sv : s) {
        jarr.push_back(sv);
    }

    nlohmann::json jv { { "values", std::move(jarr) } };

    std::cout << jv.dump() << '\n';

    return 0;
}

这打印{"values":["c","b","a"]}

您甚至可以将任意类型(甚至是用户定义的类型)直接序列化和反序列化为 JSON:

#include <iostream>
#include <string_view>
#include <unordered_set>

#include <nlohmann/json.hpp>

int main() {
    const std::unordered_set<std::string_view> s { "a", "b", "c" };

    nlohmann::json jv { { "values", s } };

    std::cout << jv.dump() << '\n';

    return 0;
}

1:根据我的经验,除非您必须每秒解析数百万个 JSON,否则大多数性能考虑都变得毫无意义。我已经看到在 ESP32 上使用了 nlohmann 的 JSON 库,即使在那里它也足够快,可以进行轻度到中度的 JSON 解析和生成。

【讨论】:

  • 我确实使用 nlohmann,这与 nlohmann 无关,因为不涉及解析或序列化。我只需要在 sqlite 生成的 JSON 中格式化 {0} 并发送它。
  • @grizzly 正如我在回答开头所说的那样,你的问题真的很不清楚,你一开始就没有提到 SQLite。
  • 至少我说过它们不是我生成的,我在上面的一个 cmets 中也提到过。无论如何,感谢您的努力。
  • 顺便说一句,您应该永远使用任意字符串作为 fmt::format 或 std::printf 或其他格式的格式,这是一个安全风险。如果{ 不在格式中,而是在您尝试用作参数的一个字符串中,它们不会对{fmt} 造成任何麻烦。如果您尝试以任何有意义的形式编辑 SQLite 返回的 JSON,则应先对其进行解析,对其进行编辑,然后再次对其进行重新序列化,否则它不健全的可能性会变得非常高。
  • @grizzly 不客气。它很复杂,因为 JSON 是一种定义良好的序列化语言,具有精确的语义和规则,因此您不能仅通过字符串操作来安全地对其进行操作。
【解决方案2】:

我有时也使用 fmt 来格式化小型 JSON sn-ps。关于这样做的陷阱的其他 cmets 是有效的,但有时它不是一个关键组件,它可以完成工作。

来自 fmt 文档:

https://fmt.dev/latest/api.html#printf-api

头文件 fmt/printf.h 提供类似 printf 的格式化功能。以下函数使用 printf 格式字符串语法位置参数的 POSIX 扩展名

这里描述了位置参数扩展:

https://en.wikipedia.org/wiki/Printf_format_string#Parameter_field

您可以用%n$... 代替%...,将n 替换为您要使用的参数。

例如:

std::string json = fmt::sprintf(R"({ "key": [%2$d, %1$d] })", 42, 17);
std::cout << json << std::endl; // prints { "key": [17, 42] }

演示:https://godbolt.org/z/9dd734hxG

【讨论】:

    【解决方案3】:
    std::string json = fmt::format("{{ \"key\": [{}, {}] }}", 42, 17);
    std::cout << json << std::endl; // prints { "key": [17, 42] }
    

    演示:https://godbolt.org/z/4KjTPq4T5

    fmt 选择转义大括号的约定是{{}}。反斜杠,例如 \{,不起作用。虽然字符串插值的双括号很常见,例如 {{ some_var }},但使用双括号作为转义序列在cpp中有些独特(请参阅cppformat)。

    【讨论】:

      猜你喜欢
      • 2021-12-02
      • 2023-03-29
      • 1970-01-01
      • 2012-07-25
      • 1970-01-01
      • 1970-01-01
      • 2021-08-05
      • 2015-04-29
      • 1970-01-01
      相关资源
      最近更新 更多