Repository: bitcoin/bitcoin
File: src/util/string.h (C/C++)

Code before:
                // Percent escape: %%
                continue;
            }

            auto add_arg = [&] {
                unsigned maybe_num{0};
                while ('0' <= *it && *it <= '9') {
                    maybe_num *= 10;
                    maybe_num += *it - '0';
                    ++it;
                }

                if (*it == '$') {
                    ++it;
                    // Positional specifier, like %8$s
                    if (maybe_num == 0) throw "Positional format specifier must have position of at least 1";
                    count_pos = std::max(count_pos, maybe_num);
                } else {
                    // Non-positional specifier, like %s
                    ++count_normal;
                }
            };

            // Increase argument count and consume positional specifier, if present.
            add_arg();

            // Consume flags.
            while (*it == '#' || *it == '0' || *it == '-' || *it == ' ' || *it == '+') ++it;

            auto parse_size = [&] {
                if (*it == '*') {
                    ++it;
                    add_arg();
                } else {
                    while ('0' <= *it && *it <= '9') ++it;
                }
            };

            // Consume dynamic or static width value.
            parse_size();

            // Consume dynamic or static precision value.
            if (*it == '.') {
                ++it;
                parse_size();
            }

            if (*it == '\0') throw "Format specifier incorrectly terminated by end of string";

            // The remainder "[flags][width][.precision][length]type" of the
            // specifier is not checked. Parsing continues with the next '%'.

Review comment (refactor):
Given that we have two separate number "parsers" (one that keeps the result and one that throws it away), we might as well extract number parsing to a local lambda like you did with the other ones.

<details>
<summary>Diff</summary>

```patch
diff --git a/src/util/string.h b/src/util/string.h
--- a/src/util/string.h	(revision ecc5cb9a89c6b001df839675b23d8fc1f7ac69ba)
+++ b/src/util/string.h	(date 1731267170701)
@@ -45,14 +45,16 @@
                 continue;
             }
 
+            auto parse_number = [&] {
+                unsigned num{0};
+                for (; '0' <= *it && *it <= '9'; ++it) {
+                    num = num * 10 + (*it - '0');
+                }
+                return num;
+            };
+
             auto add_arg = [&] {
-                unsigned maybe_num{0};
-                while ('0' <= *it && *it <= '9') {
-                    maybe_num *= 10;
-                    maybe_num += *it - '0';
-                    ++it;
-                }
-
+                unsigned maybe_num = parse_number();
                 if (*it == '$') {
                     ++it;
                     // Positional specifier, like %8$s
@@ -75,7 +77,7 @@
                     ++it;
                     add_arg();
                 } else {
-                    while ('0' <= *it && *it <= '9') ++it;
+                    parse_number();
                 }
             };
```

</details>

Code after:
                    ++it;
                }

                if (*it == '$') {
                    ++it;
                    // Positional specifier, like %8$s
                    if (maybe_num == 0) throw "Positional format specifier must have position of at least 1";
                    count_pos = std::max(count_pos, maybe_num);
                } else {
                    // Non-positional specifier, like %s
                    ++count_normal;
                }
            };

            // Increase argument count and consume positional specifier, if present.
            add_arg();

            // Consume flags.
            while (*it == '#' || *it == '0' || *it == '-' || *it == ' ' || *it == '+') ++it;

            auto parse_size = [&] {
                if (*it == '*') {
                    ++it;
                    add_arg();
                } else {
                    while ('0' <= *it && *it <= '9') ++it;
                }
            };

            // Consume dynamic or static width value.
            parse_size();

            // Consume dynamic or static precision value.
            if (*it == '.') {
                ++it;
                parse_size();
            }

            if (*it == '\0') throw "Format specifier incorrectly terminated by end of string";

            // Length and type in "[flags][width][.precision][length]type"
            // is not checked. Parsing continues with the next '%'.
        }
        if (count_normal && count_pos) throw "Format specifiers must be all positional or all non-positional!";
        unsigned count{count_normal | count_pos};
        if (num_params != count) throw "Format specifier count must match the argument count!";
    }
};

void ReplaceAll(std::string& in_out, const std::string& search, const std::string& substitute);