Llegué aquí en busca de respuestas a la misma pregunta. No quería dejar atrás la función scanf tampoco. Al final, construyo un zsscanf yo mismo, donde analicé el formato, examiné cada uno de los datos uno por uno y verifiqué el retorno de sscanf para ver si obtenía una lectura vacía en alguno. Este era en cierto modo mi caso particular: solo quería algunos de los campos, algunos de los cuales podrían estar vacíos y no podían suponer el separador.
#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;
}
Salida:
2: |TVC-CCTV-0002|ELECTRICAL_TopoLine_823|
2: |TVC-CCTV-0000||
se advirtió, no es totalmente probado y tiene limitaciones severas (las más obvias: sólo acepta %...s
, %...c
, %...[...]
y requiere separadores como %...[...]
; de lo contrario estaría Realmente me importa la cadena de formato, de esta manera solo me importa %
).
Sí, esa es la ruta que voy a ir si no puedo encontrar una manera de adaptar el sscanf. Todavía estoy esperando poder adaptar mi sscanf, pero por lo que puedo decir [^,] no coincide con las secuencias vacías. – Belrog
@Belrog, parece que el contador interno de 'sscanf' se atasca si la cadena contiene' ,, '. Así que es mejor usar 'strtok'. –
Realmente esperaba no tener que hacer eso. Tengo una larga lista de valores de diferentes tipos (chars/ints/floats). Eso significaría un contador de campo y una declaración de conmutación grande para asignarlo correctamente. * suspiro * – Belrog