형식 문자열을 기반으로하는 스마트 변수 확장
문제
무언가를 쓸 위치를 알기 위해 구성 파일을 읽는 데몬이 있습니다. 구성 파일에는 이와 같은 줄이 있습니다.
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 프로젝트에 링크를 게시 할 수 있습니다.
- 누군가 psuedo 코드를 게시 할 수 있습니다
나는 코드가 저를 위해 쓰여진 것을 원하지 않습니다. 나는 단지 매우 간단한 무언가가되어야한다고 생각하고 첫 번째 물기를 가져가는 데 도움이 필요합니다. 나는 정말로 생각하고 명백한 생각을 간과하는 것처럼 느낍니다.
최종 결과는 다음과 같은 부울 기능이어야합니다.
bool output_sugar(const char *fmt, int jobid, const char *jobname, struct job *j);
그런 다음 j-> outpath에서 snprintf () (snessibly)를 호출하고, 어떤 종류의 쓰레기 (즉, s, d 또는 %가 아닌 것을 뒤 따르는)가 구성 줄 (또는 그 널)에 있으면 거짓을 반환합니다. 정신 점검은 쉽습니다. 저는 인수의 숫자 (및 순서)를 정확하게하는 데 약간의 시간을 보내고 있습니다.
미리 감사드립니다. 또한 명성이 있다면이 제목을 편집하십시오. 내가 말했듯이, 한 줄로 질문을하는 방법은 잘 모르겠습니다. 나는 내가 필요한 것이 a라고 생각합니다 파서, 그러나 하나의 간단한 문자열을 처리하기 위해 완전히 날아간 Lexer / Parser를 사용하여 어색한 느낌이 듭니다.
해결책
예, 당신은 일종의 구식이 필요합니다. 그러나 복잡 할 필요는 없습니다.
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 ()을 호출합니다. 시스템에 적합한 대체 오류 전략을 선택할 수 있습니다. 나는 단순히 당신이 포함했다고 가정합니다 <assert.h>
, <stdio.h>
그리고 <string.h>
그리고 err_exit()
(variadic) 기능.
테스트 코드 ...
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
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 <stdio.h>
%}
%%
%d printf("%04d",jobid);
%s printf("%s",stripspaces(dirname));
%%
나는 당신이 dbprintf와 같은 일을 할 수있는 ODBC 래퍼를 만들었습니다 ( "blah 값에 삽입 %s %d %t %y", 여기에 재료 ...); 그러나 몇 년 전이었고 나는 그것을 물고 strtok을 사용하여 형식 문자열을 구문 분석했습니다.
옵션 수가 적고 구문 분석기의 추가 유연성과 복잡성을 원하거나 필요로하지 않으면 strstr ()를 사용하여 각 잠재적 대체 기판을 검색 할 수 있습니다.
두 가지 옵션 만있는 경우, 올바르게 주문한 상태에서 Sprintf ()를 호출 할 수있는 4 개의 브랜치 IF/Else 구조 (A, A 전 B가있는 B, B가있는 B, B와 함께 B 만 포함 할 수 있습니다. 논쟁. 그렇지 않으면 여러 sprintf () 호출을 만들고 각각은 형식 문자열의 첫 번째 교체 마커 만 대체합니다. (이것은 교체가 필요한 목록을 작성하고 외관으로 정렬하는 것을 의미합니다 ...)