簡體   English   中英

在C中串聯字符串的最謹慎方法

[英]Most carefull way to concatenate strings in C

我的固件每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

我認為Jack Whitman指出的問題是造成虛假輸出的原因:您不應將要打印的字符串作為sprintf的參數傳遞。 另一個潛在的風險是緩沖區溢出,而sprintfstrcat不能防范。 (嗯, 不能防備,因為他們不知道緩沖區有多大。)

您構建一個字符串,然后將其附加到文件中。 解決您的問題的一種方法不是創建中間字符串,而是將部分格式化的字符串直接附加到文件中。

另一個解決方案是跟蹤寫入的字符數。 除非發生錯誤,否則該信息由printf的所有變體返回,該錯誤由-1 然后,將字符串附加到字符串上或多或少會像這樣:

size_t n = 0;

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

如果使用snprintf而不是sprintf ,則還可以防止緩沖區溢出。

那有點麻煩。 s*printf函數的問題在於,它們始終從頭開始填充字符串,並在隨后的調用中將數據覆蓋到相同的輸出緩沖區,這有點不直觀,因為fprintf附加到文件並且不會覆蓋任何內容較早寫入同一輸出文件。

如果在更多情況下希望從成功調用到格式化的打印例程來構建字符串,則可以編寫一個小型框架。 下面的示例從固定大小的char緩沖區創建一個“ appender”,並依次填充它。 結果可能會被截斷以防止溢出,但是除非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;
}

我可以看到您使用sprintf(dest, "%s...", dest, ...); 連接字符串。 我剛剛再次閱讀了sprintf的聯機幫助頁,並且如果沒有明確禁止,則也沒有明確允許。

就我而言,我永遠不會那樣做。 即使工作正常,您也可以要求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);
    }

我會測試緩沖區大小。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM