Я прибыл сюда в поисках ответов на тот же вопрос. Я не хотел оставлять позади функцию scanf.
В конце концов, я сам создаю zsscanf, где я анализировал формат, sscanf просматривал все данные один за другим и проверял возвращение sscanf, чтобы увидеть, получил ли я пустое чтение в любом. Это был мой частный случай: я хотел только некоторые из полей, некоторые из которых могли бы быть пустыми, и не мог использовать разделитель.
#include <stdarg.h>
#include <stdio.h>
int zsscanf(char *data, char *format, ...)
{
va_list argp;
va_start(argp, format);
int fptr = 0, sptr = 0, iptr = 0, isptr = 0, ok, saved = 0;
char def[32];
while (1)
{
if (format[fptr] != '%')
{
ok = sscanf(&format[fptr], "%28[^%]%n", def, &iptr);
if (!ok) break;
fptr += iptr;
def[iptr] = '%';
def[iptr+1] = 'n';
def[iptr+2] = 0;
ok = sscanf(&data[sptr], def, &isptr);
if (!ok) break;
sptr += isptr;
}
else
if (format[fptr+1] == '%')
{
if (data[sptr] == '%')
{
fptr += 2;
sptr += 1;
}
else
{
ok = -1;
break;
}
}
else
{
void *savehere = NULL;
ok = sscanf(&format[fptr], "%%%28[^%]%n", &def[1], &iptr);
if (!ok) break;
fptr += iptr;
def[0] = '%';
def[iptr] = '%';
def[iptr+1] = 'n';
def[iptr+2] = 0;
isptr = 0;
if (def[1] != '*')
{
savehere = va_arg(argp, void*);
ok = sscanf(&data[sptr], def, savehere, &isptr);
if (ok == 0 && isptr == 0)
{
// Let's assume only char types. Won't hurt in other cases.
((char*)savehere)[0] = 0;
ok = 1;
}
if (ok > 0)
{
saved++;
}
}
else
{
ok = sscanf(&data[sptr], def, &isptr) == 0;
}
if (ok < 0) break;
sptr += isptr;
}
}
va_end(argp);
return saved == 0 ? ok : saved;
}
int main()
{
char *format = "%15[^\t;,]%*1[\t;,]" // NameId
"%*[^\t;,]%*1[\t;,]" // Name
"%*[^\t;,]%*1[\t;,]" // Abbreviation
"%*[^\t;,]%*1[\t;,]" // Description
"%31[^\t;,]"; // Electrical Line
char nameId[16];
char elect[32];
char *line1 = "TVC-CCTV-0002\tTVC-CCTV-0002\tTVC-CCTV-0002\tCCTV DOMO CAMERA 21-32-29\tELECTRICAL_TopoLine_823\tfoo\tbar";
char *line2 = "TVC-CCTV-0000;;;;;foo;bar;";
int ok = zsscanf(line1, format, nameId, elect);
printf ("%d: |%s|%s|\n", ok, nameId, elect);
ok = zsscanf(line2, format, nameId, elect);
printf ("%d: |%s|%s|\n", ok, nameId, elect);
return 0;
}
Выход:
2: |TVC-CCTV-0002|ELECTRICAL_TopoLine_823|
2: |TVC-CCTV-0000||
Имейте в виду, что он не полностью протестирован и имеет серьезные ограничения (наиболее очевидные из которых: принимает только %...s
, %...c
, %...[...]
и требует разделителей как %...[...]
; в противном случае мне действительно пришлось бы заботиться о форматная строка, таким образом, я забочусь только о %
).