C语言含格式说明符的字符串解析:日志格式化问题求助
Got it, let's break down why your initial sprintf attempt failed and walk through two solid solutions for your use case.
Why Your Original Code Didn't Work
Your line sprintf(testString,"%s,%s", part1, part2); just concatenates the format string (part1) and the raw parameter string (part2) into a single string. When you then call printf(testString), printf treats that combined string as a new format string—so all the %s placeholders in part1 still expect separate arguments, which you aren't providing. This leads to undefined behavior (garbage output, crashes, etc.).
The direct printf call works because you're passing the format string and each parameter as individual arguments, which printf can map to the %s placeholders correctly.
Solution 1: Manual String Replacement (Standard C)
Since you already have part1 (format string with %s) and part2 (quoted, comma-separated parameters), we can:
- Parse
part2to extract individual parameter values (strip quotes and split by commas) - Iterate through
part1and replace each%swith the corresponding parsed parameter
Here's a complete, standard C implementation:
#include <stdio.h> #include <stdlib.h> #include <string.h> // Parse part2 into an array of string arguments int parse_parameters(const char* part2, char** args, int max_args) { int arg_count = 0; const char* current = part2; while (*current && arg_count < max_args) { // Skip leading quotes and commas while (*current && (*current == '"' || *current == ',')) { current++; } if (!*current) break; // Find the end of the current parameter const char* param_end = current; while (*param_end && *param_end != '"' && *param_end != ',') { param_end++; } // Allocate and copy the parameter int param_len = param_end - current; args[arg_count] = malloc(param_len + 1); strncpy(args[arg_count], current, param_len); args[arg_count][param_len] = '\0'; arg_count++; current = param_end; } return arg_count; } // Replace %s in part1 with parsed arguments int format_result(char* dest, size_t dest_size, const char* part1, char** args, int arg_count) { char* dest_ptr = dest; size_t remaining_space = dest_size; const char* fmt_ptr = part1; int arg_idx = 0; while (*fmt_ptr && arg_idx < arg_count && remaining_space > 0) { if (*fmt_ptr == '%' && *(fmt_ptr + 1) == 's') { // Replace %s with the current argument size_t arg_len = strlen(args[arg_idx]); if (arg_len >= remaining_space) { arg_len = remaining_space - 1; // Avoid buffer overflow } strncpy(dest_ptr, args[arg_idx], arg_len); dest_ptr += arg_len; remaining_space -= arg_len; fmt_ptr += 2; // Skip past "%s" arg_idx++; } else { // Copy regular characters *dest_ptr = *fmt_ptr; dest_ptr++; fmt_ptr++; remaining_space--; } } // Ensure the result is null-terminated if (remaining_space > 0) { *dest_ptr = '\0'; } else { dest[dest_size - 1] = '\0'; } return dest_ptr - dest; } int main() { // Example input const char* part1 = "system%s disconnected: IP%s %s:%s"; const char* part2 = "\"1\",\"100.100.100.100\",\"port\",\"1000\""; char* args[10]; // Adjust size based on your max expected parameters int arg_count = parse_parameters(part2, args, 10); char result[256]; // Adjust buffer size as needed format_result(result, sizeof(result), part1, args, arg_count); printf("%s\n", result); // Output: system1 disconnected: 100.100.100.100 port 1000 // Clean up allocated memory for (int i = 0; i < arg_count; i++) { free(args[i]); } return 0; }
Solution 2: Use Glib for Easier String Handling (Linux/GTK Environments)
If you're working in an environment where you can use the Glib library (common in Linux/GTK apps), you can leverage its built-in string utilities to simplify parsing and formatting:
#include <stdio.h> #include <glib.h> int main() { const char* part1 = "system%s disconnected: IP%s %s:%s"; const char* part2 = "\"1\",\"100.100.100.100\",\"port\",\"1000\""; // Remove all quotes from part2 char* cleaned_part2 = g_strdup(part2); g_strdelimit(cleaned_part2, "\"", '\0'); // Split the cleaned string by commas gchar** args = g_strsplit(cleaned_part2, ",", -1); // Build the result string by replacing %s placeholders GString* result = g_string_new(""); const char* fmt_ptr = part1; int arg_idx = 0; while (*fmt_ptr && args[arg_idx] && strlen(args[arg_idx]) > 0) { if (*fmt_ptr == '%' && *(fmt_ptr + 1) == 's') { g_string_append(result, args[arg_idx]); fmt_ptr += 2; arg_idx++; } else { g_string_append_c(result, *fmt_ptr); fmt_ptr++; } } // Append any remaining part of the format string if (*fmt_ptr) { g_string_append(result, fmt_ptr); } printf("%s\n", result->str); // Clean up Glib resources g_string_free(result, TRUE); g_strfreev(args); g_free(cleaned_part2); return 0; }
Key Takeaways
- You can't pass a raw parameter string to
sprintf/printfand expect it to be treated as individual arguments—you need to split the parameters first. - Manual replacement is portable (works with any standard C compiler) but requires more code.
- Glib simplifies string handling if it's available in your environment.
内容的提问来源于stack exchange,提问作者m4n07

