1

私は ac プロジェクトに取り組んでおり、ディレクトリ内からファイルを削除する必要があります。なんらかの理由で、ファイルまたはディレクトリが存在しないため削除できないと言い続けます。以下は、ファイルを削除するために使用しているコードです。

void deleteOldestLog()
{
    FILE *fp;
    char path[FILE_PATH_BUF_LEN], *fileName;

    fp = popen("ls -tr /home/myfolder/logs/ |head -1", "r");
    if (fp == NULL)
    {
        printf("Failed to run command");
    }
    else
    {
        char removalPath[FILE_PATH_BUF_LEN];
        while ((fileName = fgets(path, sizeof(path)-1, fp)) != NULL)
        {
            sprintf(removalPath, "/home/myfolder/logs/%s", fileName, sizeof(fileName)-1);

            printf("Removing file: %s", removalPath);
            if (remove(removalPath) != 0)
            {
                perror("ERROR DELETING LOG");
            }
            else
            {
                printf("Successfully deleted %s", removalPath);
            }
            break;
        }
        pclose(fp);
    }
}

ファイルが存在しないためファイルが見つからないと言っていますが、これが真実ではないことはわかっています。なぜならll、cプログラムが出力したパスを実行すると、削除しようとしているファイルが返されるからです。

fgets が文字列の末尾に '\0' を配置しているため、削除が機能しなくなっている可能性があると思います。

どうすればこれを修正できますか?

4

1 に答える 1

5

によって読み取られるファイル名の末尾に改行がありますfgets()。ファイル名は実際には改行で終わっていません。

次の方法で改行を削除しようとします。

sprintf(removalPath, "/home/myfolder/%s", fileName, sizeof(fileName)-1);

ただし、効果的にするには、strlen()代わりにを使用するsizeof()必要があり、フォーマット文字列を変更する必要があります。

sprintf(removalPath, "/home/myfolder/%.*s", (int)strlen(fileName)-1, fileName);

の引数は である*必要があり、intstrlen()返しますsize_t。したがって、キャスト。(警告をオンにすると、GCC はそのようなことについて警告します。少なくとも を使用してください-Wall。)

ヒント: 疑わしい場合は、文字列を印刷してください。私は通常、このような形式を使用します。文字列を囲む山括弧に注意してください。

printf("Removing: <<%s>>\n", removalPath);

あなたが見るとき:

Removing: <</home/myfolder/something
>>

文字列の改行に問題があることを知っています。マーカーがないと、文字列に改行があり、出力に余分な改行が含まれていることに気付かないかもしれません。


フォーマット文字列を変更する必要があるのはなぜですか?

もう一度原文を見てみましょうsprintf()

sprintf(removalPath, "/home/myfolder/%s", fileName, sizeof(fileName)-1);

フォーマット文字列は、文字列である 1 つの引数を想定しています。この呼び出しは、文字列と長さの 2 つの値を提供します。ですから、最初の問題は、議論の取り残しがあるということです。これは通常ダメージを与えませんが、注意してください。おそらく、長さマイナス 1 を渡す理由は、最後の文字を失うことでした。ファミリの形式は、printf()1 つまたは 2 つの数字で装飾でき、いずれかまたは両方*に整数値の代わりに a を含めることができます。これらの数値は、フォーマットされた値の長さを制限します。あなたが書くとき:

%.*s

出力の長さはint、文字列自体の前に引数として渡された値によって正確に指定された長さになると述べています。したがって、改訂:

sprintf(removalPath, "/home/myfolder/%.*s", (int)strlen(fileName)-1, fileName);

(この情報を追加するときに修正しました。)

また、etcの出力にエラー チェックを追加していませsprintf()ん。これは珍しいことではありません。ただし、ベスト コーディング プラクティスにより、次のような関数sprintf()が期待どおりの値を返すことが保証されます (これは、末尾の null を除く、文字列に書き込まれた文字数です) '\0'

(余談ですが、一般的には、バッファ オーバーフローを回避できる ;snprintf()よりも使用する方が適切です。sprintf()

snprintf(removalPath, sizeof(removalPath), "/home/myfolder/%.*s",
         (int)strlen(fileName)-1, fileName);

ただし、*snprintf()MSVC での関数の動作は、C 標準 (C99、C11) で義務付けられている動作とは異なります。さらに悪いことに、vsnprintf_s()およびその他の_s関数の場合、引数リストは MSVC と C 標準で異なります。)

于 2012-06-20T14:02:12.197 に答える