{"id":4687,"date":"2023-09-12T11:59:37","date_gmt":"2023-09-12T09:59:37","guid":{"rendered":"https:\/\/mariusbancila.ro\/blog\/?p=4687"},"modified":"2023-09-12T11:59:40","modified_gmt":"2023-09-12T09:59:40","slug":"formatting-text-in-c-the-old-and-the-new-ways","status":"publish","type":"post","link":"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/","title":{"rendered":"Formatting Text in C++: The Old and The New Ways"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">When it comes to format a piece of text in C++ there are several ways you can employ:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>I\/O streams, particularly <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/io\/basic_stringstream\" target=\"_blank\" rel=\"noopener\" title=\"\">std::stringstream<\/a> with stream operations (such as <code>operator &lt;&lt;<\/code>)<\/li><li>printf family of functions, particularly <a href=\"https:\/\/en.cppreference.com\/w\/c\/io\/fprintf\" target=\"_blank\" rel=\"noopener\" title=\"\">sprintf<\/a><\/li><li>the C++20 format library, particularly <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/utility\/format\/format\" target=\"_blank\" rel=\"noopener\" title=\"\">std::format<\/a> \/ <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/utility\/format\/format_to\" target=\"_blank\" rel=\"noopener\" title=\"\">std::format_to<\/a><\/li><li>any 3rd party library, <a href=\"https:\/\/github.com\/fmtlib\/fmt\" target=\"_blank\" rel=\"noopener\" title=\"\">{fmt}<\/a> in particular (the source of the new standard format library)<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The first two options represent the old ways. The format library, is, obviously, the new, modern way of formatting text. But which is better in terms of performance? Let&#8217;s try to figure out.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">In which examples are presented&#8230;<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">First, let&#8217;s see a few simple examples of formatting text. Let&#8217;s say we want to format a text in the form <code>\"severity=1,error=42,reason=access denied\"<\/code>. We can do this as follows:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>with streams<\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int severity = 1;\nunsigned error = 42;\nreason = \"access denied\";\n\nstd::stringstream ss;\nss &lt;&lt; \"severity=\" &lt;&lt; severity\n   &lt;&lt; \",error=\" &lt;&lt; error\n   &lt;&lt; \",reason=\" &lt;&lt; reason;\n\nstd::string text = ss.str();<\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>with printf<\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int severity = 1;\nunsigned error = 42;\nreason = \"access denied\";\n\nstd::string text(50, '\\0');\nsprintf(text.data(), \"severity=%d,error=%u,reason=%s\", severity, error, reason);<\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>with format<\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int severity = 1;\nunsigned error = 42;\nreason = \"access denied\";\n\nstd::string text = std::format(\"severity={},error={},reason={}\", severity, error, reason);\n\n\/\/ or\n\nstd::string text;\nstd::format_to(std::back_inserter(text), \"severity={},error={},reason={}\", severity, error, reason);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>std::format<\/code> is very similar in many aspects to <code>printf<\/code>, although you don&#8217;t need to provide type specifiers, such as <code>%d<\/code>, <code>%u<\/code>, <code>%s<\/code>, only an argument placeholder <code>{}<\/code>. Of course, there are format specifiers available, which you can read about <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/utility\/format\/formatter\" target=\"_blank\" rel=\"noopener\" title=\"\">here<\/a>, but that&#8217;s not of interest for the point of this article.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>std::format_to<\/code> is useful to append text because it writes to an output buffer through an iterator. This allows us to append text, conditionally, such as in the following example where the reason is written in the message only if it&#8217;s not empty:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">std::string text = std::format(\"severity={},error={}\", severity, error);\n\nif(!reason.empty())\n  std::format_to(std::back_inserter(text), \",reason=\", reason);<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">In which performance is compared&#8230;<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">With all these options, the question is which is the best to use? In general, stream operations are slow, and <code>{fmt}<\/code> is known to be significantly faster. But not all cases are equal, and, in general, when you want to make optimizations, you should measure yourself and not take decisions based on generalities.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I asked myself this question recently, when I noticed in a project that I&#8217;m currently involved with the large scale use of <code>std::stringstream<\/code> to format log messages. Most examples involved one to three arguments. For instance:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">std::stringstream ss;\nss &lt;&lt; \"component id: \" &lt;&lt; id;\n\nstd::string msg = ss.str();\n\n\/\/ or\n\nstd::stringstream ss;\nss &lt;&lt; \"source: \" &lt;&lt; source &lt;&lt; \"|code=\" &lt;&lt; code;\n\nstd::string msg = ss.str();<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">I thought that replacing <code>std::stringstream<\/code> with <code>std::format<\/code> should be beneficial for performance, but I wanted to measure how much faster it would be. So I wrote the following program to compare the alternatives. What it does is:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>format a text to the form <code>\"Number 42 is great!\"<\/code><\/li><li>compares <code>std::stringstream<\/code>, <code>sprintf<\/code>, <code>std::format<\/code>, and <code>std::format_to<\/code><\/li><li>runs a variable number of iterations, 1 to 1000000, and determines the average time per iteration<\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int main()\n{\n   {\n      std::stringstream ss;\n      ss &lt;&lt; 42;\n   }\n\n   using namespace std::chrono_literals;\n\n   std::random_device rd{};\n   auto mtgen = std::mt19937{ rd() };\n   auto ud = std::uniform_int_distribution&lt;>{ -1000000, 1000000 };\n\n   std::vector&lt;int> iterations{ 1, 2, 5, 10, 100, 1000, 10000, 100000, 1000000 };\n\n   std::println(\"{:>10} {:>12} {:>7} {:>9} {:>6}\", \"iterations\", \"stringstream\", \"sprintf\", \"format_to\", \"format\");\n   std::println(\"{:>10} {:>12} {:>7} {:>9} {:>6}\", \"----------\", \"------------\", \"-------\", \"---------\", \"------\");\n\n   for (int count : iterations)\n   {\n      std::vector&lt;int> numbers(count);\n      for (std::size_t i = 0; i &lt; numbers.size(); ++i)\n      {\n         numbers[i] = ud(mtgen);\n      }\n\n      long long t1, t2, t3, t4;\n\n      {\n         auto start = std::chrono::high_resolution_clock::now();\n\n         for (std::size_t i = 0; i &lt; numbers.size(); ++i)\n         {\n            std::stringstream ss;\n            ss &lt;&lt; \"Number \" &lt;&lt; numbers[i] &lt;&lt; \" is great!\";\n            std::string s = ss.str();\n         }\n\n         auto end = std::chrono::high_resolution_clock::now();\n         t1 = std::chrono::duration_cast&lt;std::chrono::nanoseconds>(end - start).count();\n      }\n\n      {\n         auto start = std::chrono::high_resolution_clock::now();\n\n         for (std::size_t i = 0; i &lt; numbers.size(); ++i)\n         {\n            std::string str(100, '\\0');\n            std::sprintf(str.data(), \"Number %d is great!\", numbers[i]);\n         }\n\n         auto end = std::chrono::high_resolution_clock::now();\n         t2 = std::chrono::duration_cast&lt;std::chrono::nanoseconds>(end - start).count();\n      }\n\n      {\n         auto start = std::chrono::high_resolution_clock::now();\n\n         for (std::size_t i = 0; i &lt; numbers.size(); ++i)\n         {\n            std::string s;\n            std::format_to(std::back_inserter(s), \"Number {} is great!\", numbers[i]);\n         }\n\n         auto end = std::chrono::high_resolution_clock::now();\n         t3 = std::chrono::duration_cast&lt;std::chrono::nanoseconds>(end - start).count();\n      }\n\n      {\n         auto start = std::chrono::high_resolution_clock::now();\n\n         for (std::size_t i = 0; i &lt; numbers.size(); ++i)\n         {\n            std::string s = std::format(\"Number {} is great!\", numbers[i]);\n         }\n\n         auto end = std::chrono::high_resolution_clock::now();\n         t4 = std::chrono::duration_cast&lt;std::chrono::nanoseconds>(end - start).count();\n      }\n\n      std::println(\"{:&lt;10} {:&lt;12.2f} {:&lt;7.2f} {:&lt;9.2f} {:&lt;7.2f}\", count, t1\/1000.0 \/ count, t2 \/ 1000.0 \/ count, t3 \/ 1000.0 \/ count, t4 \/ 1000.0 \/ count);\n   }\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The results vary slightly for each execution, and, as one would expect, should be different on different machines. On my machine, a Release build for 64-bit produces results as following (time values are given in microseconds):<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">iterations stringstream sprintf format_to format\n---------- ------------ ------- --------- ------\n1          29.60        11.80   1.80      0.60\n2          10.00        4.20    0.55      0.50\n5          1.56         0.56    0.34      0.26\n10         1.61         1.15    0.26      0.31\n100        1.15         0.28    0.22      0.26\n1000       1.17         0.30    0.24      0.26\n10000      1.29         0.28    0.23      0.24\n100000     0.87         0.18    0.15      0.16\n1000000    0.74         0.18    0.15      0.16<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If we ran the loop a single time, the <code>sprintf<\/code> is 2-3 times faster, in general, than <code>std::stringstream<\/code>, but <code>std::format<\/code>\/<code>std::format<\/code> are 20-30x times faster than <code>std::stringstream<\/code> and 5-20x faster than <code>sprintf<\/code>. These numbers change as we measure the execution of more loops, but still, <code>std::format<\/code> is still about 5 times faster than <code>std::stringstream<\/code> and mostly the same with <code>sprintf<\/code>. Since in my case generating a log message does not occur in a loop, I can conclude that the speed-up can be 20-30x.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For the case when 2 arguments are written in the output text, the numbers are similar. The program is only slightly different, to generate text of the form <code>\"Numbers 42 and 43 are great!\"<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int main()\n{\n   {\n      std::stringstream ss;\n      ss &lt;&lt; 42;\n   }\n\n   using namespace std::chrono_literals;\n\n   std::random_device rd{};\n   auto mtgen = std::mt19937{ rd() };\n   auto ud = std::uniform_int_distribution&lt;>{ -1000000, 1000000 };\n\n   std::vector&lt;int> iterations{ 1, 2, 5, 10, 100, 1000, 10000, 100000, 1000000 };\n\n   std::println(\"{:>10} {:>12} {:>7} {:>9} {:>6}\", \"iterations\", \"stringstream\", \"sprintf\", \"format_to\", \"format\");\n   std::println(\"{:>10} {:>12} {:>7} {:>9} {:>6}\", \"----------\", \"------------\", \"-------\", \"---------\", \"------\");\n\n   for (int count : iterations)\n   {\n      std::vector&lt;int> numbers(count);\n      for (std::size_t i = 0; i &lt; numbers.size(); ++i)\n      {\n         numbers[i] = ud(mtgen);\n      }\n\n      long long t1, t2, t3, t4;\n\n      {\n         auto start = std::chrono::high_resolution_clock::now();\n\n         for (std::size_t i = 0; i &lt; numbers.size(); ++i)\n         {\n            std::stringstream ss;\n            ss &lt;&lt; \"Numbers \" &lt;&lt; numbers[i] &lt;&lt; \" and \" &lt;&lt; numbers[i] + 1 &lt;&lt; \" are great!\";\n            std::string s = ss.str();\n         }\n\n         auto end = std::chrono::high_resolution_clock::now();\n         t1 = std::chrono::duration_cast&lt;std::chrono::nanoseconds>(end - start).count();\n      }\n\n      {\n         auto start = std::chrono::high_resolution_clock::now();\n\n         for (std::size_t i = 0; i &lt; numbers.size(); ++i)\n         {\n            std::string str(100, '\\0');\n            sprintf(str.data(), \"Numbers %d and %d are great!\", numbers[i], numbers[i] + 1);\n         }\n\n         auto end = std::chrono::high_resolution_clock::now();\n         t2 = std::chrono::duration_cast&lt;std::chrono::nanoseconds>(end - start).count();\n      }\n\n      {\n         auto start = std::chrono::high_resolution_clock::now();\n\n         for (std::size_t i = 0; i &lt; numbers.size(); ++i)\n         {\n            std::string s;\n            std::format_to(std::back_inserter(s), \"Numbers {} and {} are great!\", numbers[i], numbers[i] + 1);\n         }\n\n         auto end = std::chrono::high_resolution_clock::now();\n         t3 = std::chrono::duration_cast&lt;std::chrono::nanoseconds>(end - start).count();\n      }\n\n      {\n         auto start = std::chrono::high_resolution_clock::now();\n\n         for (std::size_t i = 0; i &lt; numbers.size(); ++i)\n         {\n            std::string s = std::format(\"Numbers {} and {} are great!\", numbers[i], numbers[i] + 1);\n         }\n\n         auto end = std::chrono::high_resolution_clock::now();\n         t4 = std::chrono::duration_cast&lt;std::chrono::nanoseconds>(end - start).count();\n      }\n\n      std::println(\"{:&lt;10} {:&lt;12.2} {:&lt;7.2} {:&lt;9.2} {:&lt;7.2}\", count, t1 \/ 1000.0 \/ count, t2 \/ 1000.0 \/ count, t3 \/ 1000.0 \/ count, t4 \/ 1000.0 \/ count);\n   }\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The results are in the same range as shown previously (although, again, they vary from one execution to another):<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">iterations stringstream sprintf format_to format\n---------- ------------ ------- --------- ------\n1          27           4.7     5.8       0.8\n2          8.1          1.4     0.9       0.75\n5          3.4          0.8     0.62      0.46\n10         4.3          0.82    0.44      0.38\n100        1.9          0.45    0.31      0.33\n1000       1.9          0.46    0.37      0.35\n10000      1.8          0.38    0.29      0.31\n100000     1.3          0.26    0.22      0.24\n1000000    1.2          0.27    0.23      0.25<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">In which compatibility of formatting is discussed&#8230;<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Although in most cases, moving from <code>std::stringstream<\/code> to <code>std::format<\/code> is straight forward, there are some things that are not the same and, therefore, require extra work. Examples include formatting pointers and arrays of unsigned characters.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can easily write the value of a pointer to an output buffer as follows:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int a = 42;\n\nstd::stringstream ss;\nss &lt;&lt; \"address=\" &lt;&lt; &amp;a;\nstd::string text = ss.str();<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The text will have the form <code>\"address=00000004D4DAE218\"<\/code>. But this does not work with <code>std::format<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int a = 42;\n\nstd::string text = std::format(\"address={}\", &amp;a); \/\/ error, does not know how to format<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This snippet will generate errors (that vary with the compiler) because it does not know how to format the pointer. You can obtain the same result as previously, treating the pointer like a <code>std:size_t<\/code> value and using a format specifier such as <code>:016X<\/code> (16 hexadecimal uppercase digits with leading zeros):<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">std::string text = std::format(\"address={:016X}\", reinterpret_cast&lt;std::size_t>(&amp;a));<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now, the result will be the same (although you should keep in mind that for 32-bit the pointers are only 8 hexadecimal digits).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here is another example with arrays of unsigned characters, that <code>std::stringstream<\/code> converts to <code>char<\/code> when writing to the output buffer:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">unsigned char str[]{3,4,5,6,0};\n\nstd::stringstream ss;\nss &lt;&lt; \"str=\" &lt;&lt; str;\nstd::string text = ss.str();<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The content of text will be <code>\"str=\u2665\u2666\u2663\u2660\"<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Trying the same with <code>std::format<\/code>, will fail again because it does not know how to format the array:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">std::string text = std::format(\"str={}\", str); \/\/ error, does not now how to format<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can write the content of the array in a loop, as follows:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">std::string text = \"str=\";\nfor (auto c : str)\n   std::format_to(std::back_inserter(text), \"{}\", c);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The content of the text will be <code>\"str=34560\"<\/code> because every <code>unsigned char<\/code> is written as such to the output buffer, without any casting. To obtain the same result as previously, you need to perform a cast explicitly:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">std::string text = \"str=\";\nfor (auto c : str)\n   std::format_to(std::back_inserter(text), \"{}\", static_cast&lt;char>(c));<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Bonus talk<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re formatting text to be written to the output console and use the result of <code>std::format<\/code> \/ <code>std::format_to<\/code> with <code>std::cout<\/code> (or other alternatives) there is no need for that in C++23, which introduced <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/io\/print\" target=\"_blank\" rel=\"noopener\" title=\"\">std::print<\/a> and <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/io\/println\" target=\"_blank\" rel=\"noopener\" title=\"\">std::println<\/a>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int severity = 1;\nunsigned error = 42;\nreason = \"access denied\";\n\nstd::println(\"severity={},error={},reason={}\", severity, error, reason);<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>When it comes to format a piece of text in C++ there are several ways you can employ: I\/O streams, particularly std::stringstream with stream operations (such as operator &lt;&lt;) printf family of functions, particularly sprintf the C++20 format library, particularly std::format \/ std::format_to any 3rd party library, {fmt} in particular (the source of the new &#8230; <a title=\"Formatting Text in C++: The Old and The New Ways\" class=\"read-more\" href=\"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/\" aria-label=\"Read more about Formatting Text in C++: The Old and The New Ways\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","advgb_blocks_editor_width":"","advgb_blocks_columns_visual_guide":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[7],"tags":[451,543,646],"class_list":["post-4687","post","type-post","status-publish","format-standard","hentry","category-c","tag-c","tag-c20","tag-format"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.3 - aioseo.com -->\n\t<meta name=\"description\" content=\"A comparison of different alternatives to format text to an output buffer in C++20\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Marius Bancila\"\/>\n\t<meta name=\"keywords\" content=\"c++,c++20,format\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Marius Bancila&#039;s Blog | About code. Mostly on C++\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Formatting Text in C++: The Old and The New Ways\" \/>\n\t\t<meta property=\"og:description\" content=\"A comparison of different alternatives to format text to an output buffer in C++20\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2023-09-12T09:59:37+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2023-09-12T09:59:40+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Formatting Text in C++: The Old and The New Ways\" \/>\n\t\t<meta name=\"twitter:description\" content=\"A comparison of different alternatives to format text to an output buffer in C++20\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2023\\\/09\\\/12\\\/formatting-text-in-c-the-old-and-the-new-ways\\\/#article\",\"name\":\"Formatting Text in C++: The Old and The New Ways\",\"headline\":\"Formatting Text in C++: The Old and The New Ways\",\"author\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\"},\"datePublished\":\"2023-09-12T11:59:37+02:00\",\"dateModified\":\"2023-09-12T11:59:40+02:00\",\"inLanguage\":\"en-US\",\"commentCount\":5,\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2023\\\/09\\\/12\\\/formatting-text-in-c-the-old-and-the-new-ways\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2023\\\/09\\\/12\\\/formatting-text-in-c-the-old-and-the-new-ways\\\/#webpage\"},\"articleSection\":\"C++, C++, c++20, format\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2023\\\/09\\\/12\\\/formatting-text-in-c-the-old-and-the-new-ways\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"name\":\"IT\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"position\":2,\"name\":\"IT\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"name\":\"Software\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"position\":3,\"name\":\"Software\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"name\":\"C++\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"name\":\"IT\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"position\":4,\"name\":\"C++\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2023\\\/09\\\/12\\\/formatting-text-in-c-the-old-and-the-new-ways\\\/#listItem\",\"name\":\"Formatting Text in C++: The Old and The New Ways\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"name\":\"Software\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2023\\\/09\\\/12\\\/formatting-text-in-c-the-old-and-the-new-ways\\\/#listItem\",\"position\":5,\"name\":\"Formatting Text in C++: The Old and The New Ways\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"name\":\"C++\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\",\"name\":\"Marius Bancila's Blog\",\"description\":\"About code. Mostly on C++\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/\",\"name\":\"Marius Bancila\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2023\\\/09\\\/12\\\/formatting-text-in-c-the-old-and-the-new-ways\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a84dd2831d955c38355ddea55df4df260809b88f36408bc14fd4eab8f7f131c9?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Marius Bancila\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2023\\\/09\\\/12\\\/formatting-text-in-c-the-old-and-the-new-ways\\\/#webpage\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2023\\\/09\\\/12\\\/formatting-text-in-c-the-old-and-the-new-ways\\\/\",\"name\":\"Formatting Text in C++: The Old and The New Ways\",\"description\":\"A comparison of different alternatives to format text to an output buffer in C++20\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2023\\\/09\\\/12\\\/formatting-text-in-c-the-old-and-the-new-ways\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"datePublished\":\"2023-09-12T11:59:37+02:00\",\"dateModified\":\"2023-09-12T11:59:40+02:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\",\"name\":\"Marius Bancila's Blog\",\"description\":\"About code. Mostly on C++\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Formatting Text in C++: The Old and The New Ways","description":"A comparison of different alternatives to format text to an output buffer in C++20","canonical_url":"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/","robots":"max-image-preview:large","keywords":"c++,c++20,format","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/#article","name":"Formatting Text in C++: The Old and The New Ways","headline":"Formatting Text in C++: The Old and The New Ways","author":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"publisher":{"@id":"https:\/\/mariusbancila.ro\/blog\/#organization"},"datePublished":"2023-09-12T11:59:37+02:00","dateModified":"2023-09-12T11:59:40+02:00","inLanguage":"en-US","commentCount":5,"mainEntityOfPage":{"@id":"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/#webpage"},"isPartOf":{"@id":"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/#webpage"},"articleSection":"C++, C++, c++20, format"},{"@type":"BreadcrumbList","@id":"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/#listItem","position":1,"name":"Home","item":"https:\/\/mariusbancila.ro\/blog\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","name":"IT"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","position":2,"name":"IT","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","name":"Software"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","position":3,"name":"Software","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","name":"C++"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","name":"IT"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","position":4,"name":"C++","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/#listItem","name":"Formatting Text in C++: The Old and The New Ways"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","name":"Software"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/#listItem","position":5,"name":"Formatting Text in C++: The Old and The New Ways","previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","name":"C++"}}]},{"@type":"Organization","@id":"https:\/\/mariusbancila.ro\/blog\/#organization","name":"Marius Bancila's Blog","description":"About code. Mostly on C++","url":"https:\/\/mariusbancila.ro\/blog\/"},{"@type":"Person","@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author","url":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/","name":"Marius Bancila","image":{"@type":"ImageObject","@id":"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/a84dd2831d955c38355ddea55df4df260809b88f36408bc14fd4eab8f7f131c9?s=96&d=mm&r=g","width":96,"height":96,"caption":"Marius Bancila"}},{"@type":"WebPage","@id":"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/#webpage","url":"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/","name":"Formatting Text in C++: The Old and The New Ways","description":"A comparison of different alternatives to format text to an output buffer in C++20","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/mariusbancila.ro\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/#breadcrumblist"},"author":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"creator":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"datePublished":"2023-09-12T11:59:37+02:00","dateModified":"2023-09-12T11:59:40+02:00"},{"@type":"WebSite","@id":"https:\/\/mariusbancila.ro\/blog\/#website","url":"https:\/\/mariusbancila.ro\/blog\/","name":"Marius Bancila's Blog","description":"About code. Mostly on C++","inLanguage":"en-US","publisher":{"@id":"https:\/\/mariusbancila.ro\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"Marius Bancila's Blog | About code. Mostly on C++","og:type":"article","og:title":"Formatting Text in C++: The Old and The New Ways","og:description":"A comparison of different alternatives to format text to an output buffer in C++20","og:url":"https:\/\/mariusbancila.ro\/blog\/2023\/09\/12\/formatting-text-in-c-the-old-and-the-new-ways\/","article:published_time":"2023-09-12T09:59:37+00:00","article:modified_time":"2023-09-12T09:59:40+00:00","twitter:card":"summary","twitter:title":"Formatting Text in C++: The Old and The New Ways","twitter:description":"A comparison of different alternatives to format text to an output buffer in C++20"},"aioseo_meta_data":{"post_id":"4687","title":null,"description":"A comparison of different alternatives to format text to an output buffer in C++20","keywords":[],"keyphrases":{"focus":[],"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":[],"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","location":null,"local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":null,"created":"2023-09-12 08:35:08","updated":"2026-10-18 15:43:45","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"author_meta":{"display_name":"Marius Bancila","author_link":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/"},"featured_img":null,"jetpack_publicize_connections":[],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false},"uagb_author_info":{"display_name":"Marius Bancila","author_link":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/"},"uagb_comment_info":5,"uagb_excerpt":"When it comes to format a piece of text in C++ there are several ways you can employ: I\/O streams, particularly std::stringstream with stream operations (such as operator &lt;&lt;) printf family of functions, particularly sprintf the C++20 format library, particularly std::format \/ std::format_to any 3rd party library, {fmt} in particular (the source of the new&hellip;","coauthors":[],"tax_additional":{"categories":{"linked":["<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">C++<\/span>"]},"tags":{"linked":["<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">c++20<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">format<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">C++<\/span>","<span class=\"advgb-post-tax-term\">c++20<\/span>","<span class=\"advgb-post-tax-term\">format<\/span>"]}},"comment_count":"5","relative_dates":{"created":"Posted 3 years ago","modified":"Updated 3 years ago"},"absolute_dates":{"created":"Posted on September 12, 2023","modified":"Updated on September 12, 2023"},"absolute_dates_time":{"created":"Posted on September 12, 2023 11:59 am","modified":"Updated on September 12, 2023 11:59 am"},"featured_img_caption":"","series_order":"","jetpack_shortlink":"https:\/\/wp.me\/pYNdv-1dB","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/4687","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/comments?post=4687"}],"version-history":[{"count":7,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/4687\/revisions"}],"predecessor-version":[{"id":4694,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/4687\/revisions\/4694"}],"wp:attachment":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/media?parent=4687"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/categories?post=4687"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/tags?post=4687"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}