4

何かを書き込む場所を知るために構成ファイルを読み取るデーモンがあります。構成ファイルには、次のような行があります。

output = /tmp/foo/%d/%s/output

または、次のようになります。

output = /tmp/foo/%s/output/%d

...または単にこのように:

output = /tmp/foo/%s/output

...または最後に:

output = /tmp/output

プログラム内にcfg->pathfmtとしてその行があります。私が今やろうとしているのは、それを賢く使う方法を考え出すことです。

もう少し説明すると、パスにはフォーマットするコンポーネントを最大2つ含めることができます。%dはジョブID(int)として展開され、%sはジョブ名(string)として展開されます。ユーザーは、構成ファイルで1つ、両方、または何も使用したくない場合があります。最終的にsnprintf()に渡す前に、彼らが何をどのような順序で望んでいるかを知る必要があります。少し絞り込むことはできますが、strtok()と話したいので、醜いようです。

私はユーザーにこの種の柔軟性を提供したいのですが、それを実装するための賢明でポータブルな方法を探すことに迷っています。私はまた、これを探し始める方法について完全にそして完全に途方に暮れています。

次の場合はとても嬉しいです:

  • 誰かが私が良い例を見つけるために検索フレーズを絞り込むのを手伝ってくれるかもしれません
  • 誰かがこれを実装するOSSプロジェクトへのリンクを投稿する可能性があります
  • 誰かが疑似コードを投稿する可能性があります

私は自分のためにコードを書いてほしくありません。私は(私が思うに)非常に単純なものであるべきものに本当に固執していて、最初の一口を取るのに助けが必要です。当たり前のことを考えすぎて見落としているような気がします。

最終結果は、次のようなブール関数になります。

bool output_sugar(const char *fmt, int jobid, const char *jobname, struct job *j);

次に、j-> outpathでsnprintf()を(適切に)呼び出し、ある種のガベージ(つまり、%の後にs、d、または%以外のものが続く)が構成行(またはそのnull)にある場合はfalseを返します。健全性チェックは簡単です。フォーマットを正しくするための引数の数(および順序)を取得するのに少し時間がかかっています。

前もって感謝します。また、評判がよければ、このタイトルを自由に編集してください。私が言ったように、1行で質問する方法がよくわかりません。必要なのはパーサーだと思いますが、本格的なレクサー/パーサーを使用して1つの単純な文字列を処理するのは厄介です。

4

3 に答える 3

8

はい、ある種のパーサーが必要です。ただし、複雑である必要はありません。

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';
}

今テストされたコード。ユーザーが出力に単一の「%」を埋め込むことができるように、「%%」表記がサポートされていることに注意してください。また、文字列の末尾にある 1 つの '%' を有効であり、'%%' と同等のものとして扱います。エラー時に err_exit() を呼び出します。システムに合わせて代替エラー戦略を選択できます。<assert.h><stdio.h>および(可変個の)関数<string.h>のヘッダーが含まれていると仮定します。err_exit()


テストコード...

#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);
}
于 2009-04-05T05:06:24.817 に答える
5

strtokを使用すると、エラーが発生しやすくなります。(fl)lexとyaccを使用して、変数をミニ言語として扱うことができます。 ここに簡単なチュートリアルがあります

%{
#include <stdio.h>
%}

%%
%d                      printf("%04d",jobid);
%s                      printf("%s",stripspaces(dirname));
%%

dbprintf( "insert into blah values%s%D%T%Y"、stuff here ...);のような処理を実行できるODBCラッパーを作成しました。しかし、それは何年も前のことであり、私はそれを噛み、strtokを使用してフォーマット文字列を解析しました。

于 2009-04-04T08:21:12.047 に答える
1

オプションの数が少なく、パーサーの柔軟性と複雑さを追加する必要がない場合は、strstr()を使用して潜在的な置換部分文字列を検索するだけです。

オプションが2つしかない場合は、4分岐のif / else構造(Aのみ、Bのみ、両方ともAの前にA、両方の前にB)を作成して、正しい順序でsprintf()を呼び出すことができます。引数。それ以外の場合は、複数のsprintf()呼び出しを行います。各呼び出しは、フォーマット文字列の最初の置換マーカーのみを置換します。(これは、必要な置換のリストを作成し、それらを出現順に並べ替えることを意味します...)

于 2009-04-05T04:15:48.963 に答える