最谨慎的方法来连接C中的字符串

时间:2015-04-30 11:07:45

标签: c string csv printf sd-card

我的固件在每10秒一个周期内将字符串移动到输出(SD卡上的csv文件)。问题在于,有时以非确定性方式的字符串正在改变添加不应存在的值或在中间放置空间。这与sprintf函数或该字符串的动态分配内存有关吗?

void archPolling()
{

    double archCountVal[200];
    float archDataVal[100];
    char *FilStringMeas = malloc(sizeof(char) * 2048);
    char *FilArchive = malloc(sizeof(char) * 4096);
    vArchEvent eventArch = STATE_POLLING;
    unsigned char CiphCRC[5];
    FIL FilData;
    UINT bw;
    int queueSize = 0;

    if ( xSemaphoreTake( MutexMeasurment, 200 ) == pdTRUE)
    {
        eventArch = STATE_COLLECT;
    }

    if (eventArch == STATE_COLLECT)
    {

        while (uxQueueMessagesWaiting(xDataQueue) > 0)
        {
            xQueueReceive(xDataQueue, &archDataVal[queueSize], 0);
            queueSize++;
        }
        xSemaphoreGive(MutexMeasurment);

        if (queueSize > 0 && timerFlag == 1)
            eventArch = STATE_FORM;
        else
            eventArch = STATE_POLLING;
    }

    if (eventArch == STATE_FORM)
    {
        //portENTER_CRITICAL();
        HAL_RTC_GetTime(&RtcHandle, &RTCTimeArch, FORMAT_BIN);
        HAL_RTC_GetDate(&RtcHandle, &RTCDateArch, FORMAT_BIN);
        sprintf(FilArchive, "%02d-%02d-%02d,%02d:%02d:%02d,1", RTCDateArch.Date, RTCDateArch.Month, RTCDateArch.Year, RTCTimeArch.Hours, RTCTimeArch.Minutes, RTCTimeArch.Seconds);
        sprintf(FilStringMeas, ",");
        for (int i = 0; i < queueSize; i++)
        {
            sprintf(FilStringMeas, "%s%f,", FilStringMeas, archDataVal[i]);
        }

        strcat(FilArchive, FilStringMeas);
        archCRC((BYTE *) FilArchive, strlen(FilArchive), CiphCRC);
        strcat(FilArchive, (char *) CiphCRC);
        strcat(FilArchive, "\n");
        //portEXIT_CRITICAL();
        eventArch = STATE_SYNC;
    }

    if (eventArch == STATE_SYNC)
    {
        f_open(&FilData, "0:55AD001.csv", FA_OPEN_EXISTING | FA_WRITE);
        f_lseek(&FilData, f_size(&FilData));
        f_write(&FilData, FilArchive, strlen(FilArchive) * sizeof(char), &bw);
        f_close(&FilData);
        timerFlag = 0;
        eventArch = STATE_POLLING;
    }

    free(FilStringMeas);
    free(FilArchive);
}

编辑:错误输出的示例

0,0,1,3.512586,42.960911,,46.487427,24.501009,1.512586,27.498940,40.960911,36.598400,11.039062,9.401555,25.498940,42.487427,20.501009,7.512586,17.401556,36.960911,32.598400,7.039061,5.512586,31.498940,48.487427,16.501009,5.039061,13.401555,29.498940,46.487427,24.501009,1.512586,27.498940,44.487427,36.598400,11.039062,9.401555,38.960911,42.487427,20.501009,7.512586,33.498940,36.960911,32.598400,7.039061,15.401555,31.498940,48.487427,16.501009,3.512586,13.401555,42.960911,38.598400,3.039061,1.512586,27.498940,44.487427,22.501009,11.09062,AAAA

0,0,1,34.471630,6.303817,15,15.528328,45.382984,32.471630,6.617044,4.303817,29.472881,47.696175,16.527170,4.617044,11.528328,41.382984,38.471630,24.527170,0.303817,25.472881,43.696175,36.471630,10.617044,17.528328,37.382984,41.696175,20.527170,8.617044,15.528328,45.382984,32.471630,6.617044,13.528328,29.472881,47.696175,16.527170,2.303817,11.528328,41.382984,38.471630,12.617044,0.303817,25.472881,43.696175,22.527170,10.617044,17.528328,37.382984,34.471630,20.527170,6.303817,31.472881,39.696175,32.471630,6.617044,13.528328,43.382984,496175,AAAA

2 个答案:

答案 0 :(得分:4)

我认为Jack Whitman指出的问题是你的虚假输出的原因:你不应该将字符串打印到sprintf作为参数。另一个潜在的风险是缓冲区溢出,sprintfstrcat没有防范。 (好吧,不能防范,因为他们不知道缓冲区的大小。)

您构建一个字符串,然后将其附加到文件中。问题的一个解决方案是不创建中间字符串,而是直接将部分格式化的字符串附加到文件中。

另一个解决方案是跟踪写入的字符数。所有printf的变量都会返回此信息,除非发生错误,由-1发出错误信号。附加到字符串然后或多或少地工作:

size_t n = 0;

n += sprintf(str + n, ...);
n += sprintf(str + n, ...);

如果您使用snprintf而不是sprintf,您还可以防止缓冲区溢出。

这有点麻烦。 s*printf函数的问题是它们总是从一开始就填充字符串,并在后续调用同一输出缓冲区时覆盖数据,这有点不直观,因为fprintf适用于文件并且不会覆盖之前写入同一输出文件的任何内容。

如果您有更多情况需要从成功调用格式化打印例程构建字符串,您可以编写一个小框架。下面的例子创建了一个&#34; appender&#34;从一个固定大小的char缓冲区,然后连续填充它。可能会截断结果以防止溢出,但除非rem为0,否则将始终生成以null结尾的字符串:

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>      // needed for va_list

struct appender {
    char *str;           // original buffer
    size_t rem;          // remaining space
    size_t n;            // (potential) characters written
};

int appprintf(struct appender *app, const char *fmt, ...)
{
    va_list args;
    char *p = app->rem ? app->str + app->n : NULL;
    int n;

    va_start(args, fmt);
    n = vsnprintf(p, app->rem, fmt, args);
    app->rem = (n < app->rem) ? app->rem - n : 0;
    app->n += n;
    va_end(args);

    return n;
}

int main()
{
    char buffer[64];
    struct appender app = { buffer, sizeof(buffer) };
    int i;

    for (i = 0; i < 100; i++) {
        appprintf(&app, " %d", i);
    }

    puts(buffer);

    return 0;
}

答案 1 :(得分:2)

我可以看到你使用sprintf(dest, "%s...", dest, ...);来连接你的字符串。我刚刚再次阅读了sprintf的manpage,如果没有明确禁止,则不能明确允许。

就我而言,我会从不这样做。即使它有效,你也要求printf机器明确地将一个(越来越长的)字符串复制到自身。

而不是:

    sprintf(FilArchive, "%02d-%02d-%02d,%02d:%02d:%02d,1", RTCDateArch.Date, RTCDateArch.Month, RTCDateArch.Year, RTCTimeArch.Hours, RTCTimeArch.Minutes, RTCTimeArch.Seconds);
    sprintf(FilStringMeas, ",");
    for (int i = 0; i < queueSize; i++)
    {
        sprintf(FilStringMeas, "%s%f,", FilStringMeas, archDataVal[i]);
    }
    strcat(FilArchive, FilStringMeas);

我愿意

    sprintf(FilArchive, "%02d-%02d-%02d,%02d:%02d:%02d,1", RTCDateArch.Date, RTCDateArch.Month, RTCDateArch.Year, RTCTimeArch.Hours, RTCTimeArch.Minutes, RTCTimeArch.Seconds);
    strcat(FilArchive, ",");
    for (int i = 0; i < queueSize; i++)
    {
        sprintf(FilStringMeas, %f,", archDataVal[i]);
        strcat(FilArchive, FilStringMeas);
    }

我会测试缓冲区大小。