问题
I am writing a program that takes a file of ordered pairs of numbers as it's input, and I want to split those ordered pairs and convert them to an integer for storage in an array.
The file could be like this:
0 1
1 4
9 11
12 45
I want to write a function that takes in the line, (assumed to be already null terminated in another part of the program), splits the numbers at the space and then stores them in a integer array of size two:
int *store = malloc(2 * sizeof(store));
I have looked into strtok
but am I correct to say that it's not thread-safe and hence not the best option in this situation? Also it won't convert the numbers to integers which is something the function also needs to have.
How can I solve this problem?
回答1:
If you want to read an unknown number of ordered pairs into an array of ordered pairs, there are a number of ways to approach this. As noted in the comment, if you can guarantee that you will always have 2-integers per line, the probably the simplest way to approach reading/converting the values to integers is with fscanf
(when you have the exact same format of data on each line is one of the only times I would recommended fscanf
over fgets
and sscanf
, and then it is only to simplify the example)
When you have an unknown number of elements to read, the basic scheme is to allocate memory for some reasonably anticipated number pairs and then realloc
as needed. Since you are reading ordered-pairs, you can simplify the process by allocating storage for an pointer-to-array-of-two-int. For example with your array of ordered-pairs op
declared as follows:
int (*op)[2] = NULL;
you can allocate storage for MXLINE
number of pairs with:
op = calloc (MXLINE, sizeof *op);
(you can use either malloc
or calloc
to allocate)
After you allocate initial memory to hold your ordered pairs, you can simply read each line with a format string containing two decimal conversion specifiers (e.g. "%d %d"
) and have fscanf
perform the conversion. You must validate 2
conversions take place and then store the values in your array.
Keep a running count of the number of pairs you add so you can realloc
when you reach your limit. An example is shown below. Don't forget to free all memory when it is no longer needed, and also run your code through a memory checker to validate your use of dynamically allocated memory.
Putting it all together in a short example using your datafile, you could do something like the following. Note: the program will read from the file given as the first argument on the command line (or from stdin
by default if no filename is given):
#include <stdio.h>
#include <stdlib.h>
#define MXLINE 2
int main (int argc, char **argv) {
int i, idx = 0, mxline = MXLINE;
int (*op)[2] = NULL;
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;
}
/* allocate/validate initial memory */
if (!(op = calloc (MXLINE, sizeof *op))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
/* read each ordered pair into 'op' array */
while (fscanf (fp, "%d %d", &op[idx][0], &op[idx][1]) == 2) {
if (++idx == mxline) { /* realloc when limit reached */
void *tmp = realloc (op, sizeof *op * mxline * 2);
if (!tmp) { /* validate reallocation */
fprintf (stderr, "error: realloc failed.\n");
break;
}
op = tmp; /* assign reallocated pointer to op */
mxline *= 2; /* update line allocation limit */
}
}
for (i = 0; i < idx; i++) /* show ordered pairs */
printf ("pair[%3d] %3d : %3d\n", i, op[i][0], op[i][1]);
free (op); /* free allocated memory */
if (fp != stdin) /* close file if not stdin */
fclose (fp);
return 0;
}
Example Use/Output
$ ./bin/array_ptr2array <dat/orderedpairs.txt
pair[ 0] 0 : 1
pair[ 1] 1 : 4
pair[ 2] 9 : 11
pair[ 3] 12 : 45
Memory/Error Check
Do not forget to validate your use of dynamically allocated memory and that your are freeing all memory when it is no longer needed:
$ valgrind ./bin/array_ptr2array <dat/orderedpairs.txt
==8107== Memcheck, a memory error detector
==8107== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==8107== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==8107== Command: ./bin/array_ptr2array
==8107==
pair[ 0] 0 : 1
pair[ 1] 1 : 4
pair[ 2] 9 : 11
pair[ 3] 12 : 45
==8107==
==8107== HEAP SUMMARY:
==8107== in use at exit: 0 bytes in 0 blocks
==8107== total heap usage: 3 allocs, 3 frees, 112 bytes allocated
==8107==
==8107== All heap blocks were freed -- no leaks are possible
==8107==
==8107== For counts of detected and suppressed errors, rerun with: -v
==8107== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
(note: the initial allocation was for MXLINE
(2
) pairs. The was intentionally done to force reallocation. In the real world you will want to minimize the number of times your must allocate memory. So if you think you could have 20 or so pairs, set the initial allocation for 24
or 32
pairs or whatever is your best reasonable guess of the number you will have (plus a couple for good-measure))
Look it over and let me know if you have any questions.
回答2:
Maybe this is a solution:
gets(str, 80, fp); // get a line
sscanf(str, "%d %d", &store[0], &store[1]); // read numbers from that line
来源:https://stackoverflow.com/questions/36299842/spilt-a-string-of-integers-based-on-delimiter-and-convert-to-int-type