简体   繁体   中英

How to use sscanf to read a line into 5 variables

I am trying to code something that will read in a formatted line and break it up into variables.

The line will look like this

label:    add    $t0,$t1,$t2

Format is

label:[tab]instruction[tab]$rd,$rs,$rt

It is a label followed by a colon, a tab, an instruction, a tab, then a list of registers just like that. I have strings declared to hold the label, the instruction, the destination register, the first register, and the source register. These variables are named lLabel, lInst, lrs, lrt, and lrd.

The line of code I have made is this

sscanf(line, "%[^:]:\t%[^\t]\t$%[^,],$%[^,]$,%[^\n]", lLabel, lInst, lrd, lrs, lrt);

My idea was to read everything up to a colon, store it in lLable. Match colon, match tab. Read everything up to tab, store in lInst. Match tab, match $ sign. Read up to comma, Store in rd. Match comma and $. Read everything up to comma, store in rs. Match comma and $. Then read everything up to newline and store in rt.

For some reason, it fills like this

Label: me
Instruction: add
rs: t1
rt:
rd: t0

Where am I going wrong with this formatting?

EDIT: I've changed it to

sscanf(line, "%[^:]:\t%[^\t]\t$%[^,],$%[^,],$%[^\n]", lLabel, lInst, lrd, lrs, lrt);

Not it fills like

Label: me
Instruction: add
rs: t1t2t0
rt: t2t0
rd: t0

As pointed out in the comments you have a typo in your sscanf format string along with questions concerning whether you provide sufficient space for the nul-terminator that will be placed at the end of each string parsed by sscanf .

One additional, and critical point is a complete failure to validate the results of the sscanf conversions by checking the return from sscanf to insure 5 conversions actually take place. It is imperative that you validate the conversions before blindly processing what very well may be garbage in your label, instruction and rX variables.

Another small consideration is how you are handling the conversion specifier for rt (eg %[^\\n] ). While that may work for each case where there are no additional spaces or characters following $t2 , it is quite a bit more restrictive than it needs to be. Since by default %s will provide a conversion up to the first whitespace characters (which includes '\\n' ), you can protect against a stray space before the newline by simply using %s as the final conversion specifier, eg

    if (sscanf (buf, "%[^:]:\t%[^\t]\t$%[^,],$%[^,],$%s",
                label, inst, rd, rs, rt) == 5)
        printf ("label: %s\ninst : %s\nrd   : %s\nrs   : %s\nrt   : %s\n\n",
                label, inst, rd, rs, rt);

Lastly, you will want to provide some way of handling lines that fail the sscanf conversion process. How and what you do in that case is up to you, but you need to protect against a failed conversion.

Putting that together into a short example, you could do something similar to the following that will read and convert each label:[tab]instruction[tab]$rd,$rs,$rt contained on separate lines in the filename provided as the first argument to the program (or from stdin if no filename is provided). A simple enum is used to declare constants for the various string lengths used within the code.

#include <stdio.h>

/* constants for max 'rX', 'label/inst' and chars in line */
enum { MAXR = 8, MAXL = 32, MAXC = 128 };

int main (int argc, char **argv) {

    char buf[MAXC] = "",    /* initialize variables */
         label[MAXL] = "", 
         inst[MAXL] = "",
         rd[MAXR] = "", 
         rs[MAXR] = "", 
         rt[MAXR] = "";
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    while (fgets (buf, MAXC, fp))   /* read each line */
        /* (you should check that a complete line was read here) */
        /* validate parse into label, inst, rd, rs, rt */
        if (sscanf (buf, "%[^:]:\t%[^\t]\t$%[^,],$%[^,],$%s",
                    label, inst, rd, rs, rt) == 5)
            printf ("label: %s\ninst : %s\nrd   : %s\nrs   : %s\nrt   : %s\n\n",
                    label, inst, rd, rs, rt);
        else    /* handle error */
            fprintf (stderr, "error: invalid conversion.\n\n");

    if (fp != stdin) fclose (fp);     /* close file if not stdin */

    return 0;
}

Example Input File

$ cat dat/instr.txt
label:  add     $t0,$t1,$t2
next:   sub     $r0,$r1,$r2
foo:    bar
last:   mul     $s0,$s1,$s2

Example Use/Output

$ ./bin/sscanffmt <dat/instr.txt
label: label
inst : add
rd   : t0
rs   : t1
rt   : t2

label: next
inst : sub
rd   : r0
rs   : r1
rt   : r2

error: invalid conversion.

label: last
inst : mul
rd   : s0
rs   : s1
rt   : s2

Look things over and let me know if you have any further questions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM