snprintf не сохраняет последний символ в буфере?

Я написал простую небольшую программу для чтения файла в следующем формате, используя snprintf,

skip first 15 chars , next 9 chars are sequence number, next 2 char is message and so on.   

Меня интересует порядковый номер и сообщение, т.е. от символа № 16 до 26;

ниже программа. Он не читает последний символ для каждого поля. Он считывает 8 символов вместо 9 для порядкового номера и 1 байт вместо 2 для сообщения.

#include<stdio.h>

typedef struct
{
  char seqno[9];
  char msg[2];
}Header_T;

int main()
{
  char buf[64]={'\0'};
  FILE *fp;
  int i = 0;
  Header_T hdr1;
  int skipbytes = 15;

  fp=fopen("asdf", "r");
  if (fp == NULL)
  {
    printf("FILE OPEN ERROR\n");
  }
  printf("--sequence--msg--\n");
  while( fgets( buf, sizeof(buf), fp ) != NULL )
  {
    i=skipbytes;
    snprintf(hdr1.seqno, 9, "%s", (buf+i));
    i+=sizeof(hdr1.seqno);
    snprintf(hdr1.msg, 2, "%s", (buf+i));
    i=0;

    printf("--%s--%s--\n", hdr1.seqno, hdr1.msg);
    memset(buf, '\0', 64 );
  }
fclose(fp);
return 0 ;
}

часть содержимого файла выглядит следующим образом

201301082323458000000001H QB234
201301082323558000000002J QB234
201301082323658000000003N QB234
201301082323758000000004JRQB234
201301082333458000000010JSQB234

поэтому ожидаемый результат

--sequence--msg--
--000000001--H --
--000000002--J --
--000000003--N --
--000000004--JR--
--000000010--JS--

Но вместо этого я получаю вывод как

--seqno--msgtype--
--00000000--H--
--00000000--J--
--00000000--N--
--00000000--J--
--00000001--J--

Может ли кто-нибудь объяснить это поведение и как его исправить?

Вместо snprintf я попробовал ту же программу, используя посимвольное назначение цикла for, и программа работает нормально; но для этого мне нужно добавить некоторые наполнители для выравнивания байтов в структуре.

Я также пытался использовать pragma pack(), но это также не имеет значения.

Я использую gcc 4.4.3 на 64-битной машине Ubuntu.


person Ashish Chavan    schedule 04.08.2013    source источник


Ответы (3)


Из cppreference

int snprintf ( char * s, size_t n, const char * format, ... );

n: Максимальное количество байтов, которые будут использоваться в буфере.

Сгенерированная строка имеет длину не более n-1, оставляя место для дополнительного завершающего нулевого символа.

Поэтому, если вы ожидаете 9 символов, вы должны передать n как 10 вместо 9:

snprintf(hdr1.seqno, 10, "%s", (buf+i));

Header_T необходимо соответствующим образом изменить:

typedef struct
{
  char seqno[10];
  char msg[3];
}Header_T;
person Yu Hao    schedule 04.08.2013
comment
@AshishChavan Header_T тоже нужно изменить, seqno должно быть 10 элементов. - person Yu Hao; 04.08.2013
comment
Перед запуском программы я увеличил размер элементов struct Header_T на 1, а затем передал n как 10, я получил вывод, подобный этому --000000001H --H -- т.е. он также включает следующие 2 символа. - person Ashish Chavan; 04.08.2013
comment
@AshishChavan Убедитесь, что seqno заканчивается нулем. - person Yu Hao; 04.08.2013

Вам нужен дополнительный символ для хранения завершающего символа '\0', поэтому ваша структура должна выглядеть так:

typedef struct
{ char seqno[10];
  char msg[3];
}Header_T;

РЕДАКТИРОВАТЬ: поскольку это простая передача данных, почему бы не использовать memcpy, например:

memset(hdr1, 0, sizeof(hdr1));
memcpy(hdr1.seqno, &buf[skipbytes], 9);
memcpy(hdr1.msg, &buf[skipbytes + 9], 2);
person Edward Clements    schedule 04.08.2013
comment
это уже пробовал. Вместо этого, если я добавлю такие наполнители, это сработает ` typedef struct { char seqno[9]; Наполнитель для обугливания[3]; символ сообщение[2]; }Заголовок_T;` - person Ashish Chavan; 04.08.2013

Этот ответ следует за двумя ответами выше. Все они дают несколько правильных предложений. Я просто соединил их вместе.

Во-первых, используйте:

typedef struct
{ char seqno[10];
  char msg[3];
}Header_T;       //so we have the memory for '\0'

Затем используйте:

   while( fgets( buf, sizeof(buf), fp ) != NULL )
   {
        i=skipbytes;
        snprintf(hdr1.seqno, 10, "%s", (buf+i));//here we read 9 number and a '\0'
        i+=sizeof(hdr1.seqno) -1;               //the  '\0' is not part of buffer.
        snprintf(hdr1.msg, 3, "%s", (buf+i));   //also for '\0'
        i=0;

        printf("--%s--%s--\n", hdr1.seqno, hdr1.msg);
        memset(buf, '\0', 64 );
   }
person Lidong Guo    schedule 04.08.2013