我有一个守护进程,它读取配置文件以便知道在哪里写东西.在配置文件中,存在如下行:
output = /tmp/foo/%d/%s/output
或者,它可能看起来像这样:
output = /tmp/foo/%s/output/%d
......或者只是这样:
output = /tmp/foo/%s/output
......或者最后:
output = /tmp/output
我在程序中将该行作为cfg-> pathfmt.我现在要做的是想出一些巧妙的使用方法.
再解释一下,路径最多可以包含两个要格式化的组件.%d将作为作业ID(int)扩展,%s将作为作业名称(字符串)扩展.用户可能希望在配置文件中使用一个,两个或不使用.在我最终将它传递给snprintf()之前,我需要知道他们想要什么,以什么顺序.我可以把它缩小范围,但我一直想和strtok()谈谈,这看起来很难看.
我希望为用户提供这种灵活性,但是我正在寻找一种合理,便携的方式来实现它.对于如何开始搜索这个问题,我也完全失败了.
如果符合以下条件,我会非常高兴:
有人可以帮我缩小搜索短语以找到好的例子
有人可以发布指向实现此功能的OSS项目的链接
有人可以发布一些伪代码
我不希望为我编写代码,我只是坚持(我认为)应该是非常简单的东西,并需要一些帮助第一口.我真的觉得我在思考并忽略了明显的东西.
最终结果应该是这样的布尔函数:
bool output_sugar(const char *fmt, int jobid, const char *jobname, struct job *j);
然后它将在j-> outpath上调用snprintf()(明智地),如果某种垃圾(即%后跟不是s,d或%的东西)在配置行(或其null)中则返回false.完整性检查很容易,我只是有点时间获取格式正确的参数的数量(和顺序).
提前致谢.此外,如果您有这样的声誉,请随时编辑此标题,正如我所说,我不太确定如何在一行中提出问题.我认为我需要的是一个解析器,但使用一个完整的词法分析器/解析器处理一个简单的字符串感觉很尴尬.
是的,你需要某种解析器.但它并不复杂:
void format_filename(const char *fmt, int jobid, const char *jobname, char *buffer, size_t buflen) { char *end = buffer + buflen - 1; const char *src = fmt; char *dst = buffer; char c; assert(buffer != 0 && fmt != 0 && buflen != 0 && jobname != 0); while ((c = *src++) != '\0') { if (dst >= end) err_exit("buffer overflow in %s(): format = %s\n", __func__, fmt); else if (c != '%') *dst++ = c; else if ((c = *src++) == '\0' || c == '%') { *dst++ = '%'; if (c == '\0') break; } else if (c == 's') { size_t len = strlen(jobname); if (len > end - dst) err_exit("buffer overflow on jobname in %s(): format = %s\n", __func__, fmt); else { strcpy(dst, jobname); dst += len; } } else if (c == 'd') { int nchars = snprintf(dst, end - dst, "%d", jobid); if (nchars < 0 || nchars >= end - dst) err_exit("format error on jobid in %s(); format = %s\n", __func__, fmt); dst += nchars; } else err_exit("invalid format character %d in %s(): format = %s\n", c, __func__, fmt); } *dst = '\0'; }
现在测试代码.请注意,它支持'%%'表示法,允许用户在输出中嵌入单个'%'.此外,它将字符串末尾的单个'%'视为有效且等效于'%%'.它在错误时调用err_exit(); 您可以选择适合您系统的其他错误策略.我只是假设你已经包含了
,
以及(variadic)函数
的标题err_exit()
.
测试代码......
#include#include #include #include static void err_exit(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); exit(1); }
......然后format_filename()
如上所述......
#define DIM(x) (sizeof(x)/sizeof(*(x))) static const char *format[] = { "/tmp/%d/name/%s", "/tmp/%s/number/%d", "/tmp/%s.%d%%", "/tmp/%", }; int main(void) { char buffer[64]; size_t i; for (i = 0; i < DIM(format); i++) { format_filename(format[i], 1234, "job-name", buffer, sizeof(buffer)); printf("fmt = %-20s; name = %s\n", format[i], buffer); } return(0); }
使用strtok很容易出错.您可以使用(fl)lex和yacc将变量视为迷你语言. 这里有简单的教程
%{ #include%} %% %d printf("%04d",jobid); %s printf("%s",stripspaces(dirname)); %%
我做了一个ODBC包装器,它可以让你做像dbprintf这样的东西("插入blah values%s%D%T%Y",这里的东西......); 但它是很多年前我咬了它并使用strtok解析了格式字符串.