问题
Title is pretty self explanatory. I'm almost sure that the end result wouldn't be a matrix as each line would have a different number of columns, so it's more like a array of arrays of variable sizes. It would also be interesting to sort the fragments by size, biggest first. This is what I've tried so far:
int main() {
char str[MAXLEN], **fragmentsList;
int number_of_strings, i, max, k;
printf("Enter .txt file name: ");
scanf("%s", str);
printf("How many strings does the file has? ");
scanf("%d", &number_of_strings);
FILE *arq;
arq = fopen(str, "r");
for (i = 0, max = 0; !feof(arq); i++) {
while (fscanf("%c") != '\n') {
max++;
}
if (max > k) {
k = max;
}
}
fclose(arq);
fragmentsList = malloc(k * sizeof(char));
*fragmentsList = malloc(number_of_strings * sizeof(char));
arq = fopen(str, "r");
for (i = 0; !feof(arq); i++) {
fscanf(arq, "%s", fragmentList[i]);
}
for (i = 0; i < number_of_strings; i++) {
printf("%s", fragmentList[i]);
}
return 0;
}
回答1:
The reading an unknown number of lines from a file into memory in C is a basic necessity. There are a couple of way to approach it, but the standard practice is to:
declare a
pointer to pointer to type
(char**
for lines in a file) to allow you to collect and reference each line after read into memory;allocate some reasonably anticipated number of pointers to begin with to avoid repeated calls to
realloc
allocating pointers for each line individually (initially allocating8, 16, 32, ..
all work fine);declare a variable to track the number of lines read, and increment for each line;
read each line of the file into a buffer (POSIX
getline
works particularly well because it itself will dynamically allocate sufficient storage to handle any line length -- freeing you from reading with a fixed buffer and having to allocate for and accumulate partial-lines until the end of the line is reached)allocate storage for each line, copy the line to the new storage, and assign the beginning address to your next pointer,
strdup
does both for you, but since it allocates, make sure you validate it succeeds;when your index reaches your current number of allocated pointers,
realloc
more pointers (generally by doubling the number, or increasing the number by3/2
-- the rate if increase isn't particularly important -- what is important is insuring you always have a valid pointer to assign the new block of memory holding your line to); andrepeat until the file is completely read.
There are a few subtleties to be aware of when reallocating memory. First never realloc
directly to to pointer being reallocated, e.g. do not do:
mypointer = realloc (mypointer, current_size * 2);
if realloc
fails, it returns NULL
and if you are assigning the return to your original pointer, you overwrite the address to your current data with NULL
creating a memory leak. Instead, always use a temporary pointer and validate realloc
succeeds before assigning the new block of memory to your original pointer, e.g.
if (filled_pointers == allocated pointers) {
void *tmp = realloc (mypointer, current_size * 2);
if (tmp == NULL) {
perror ("realloc-mypointer");
break; /* or use goto to jump out of your read loop,
* preserving access to your current data in
* the original pointer.
*/
}
mypointer = tmp;
current_size *= 2;
}
Putting the pieces altogether in an example using getline
, you can do something like the following. (note: the code expects the filename to read from as the 1st argument to your program, if no argument is given the program will read from stdin
by default)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NPTR 8 /* initial number of pointers (must be > 0) */
int main (int argc, char **argv) {
size_t ndx = 0, /* line index */
nptrs = NPTR, /* initial number of pointers */
n = 0; /* line alloc size (0, getline decides) */
ssize_t nchr = 0; /* return (no. of chars read by getline) */
char *line = NULL, /* buffer to read each line */
**lines = NULL; /* pointer to pointer to each line */
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 'nptrs' pointers */
if (!(lines = calloc (nptrs, sizeof *lines))) {
perror ("calloc - lines");
return 1;
}
/* read each line with POSIX getline */
while ((nchr = getline (&line, &n, fp)) != -1) {
if (nchr && line[nchr - 1] == '\n') /* check trailing '\n' */
line[--nchr] = 0; /* overwrite with nul-char */
char *buf = strdup (line); /* allocate/copy line */
if (!buf) { /* strdup allocates, so validate */
perror ("strdup-line");
break;
}
lines[ndx++] = buf; /* assign start address for buf to lines */
if (ndx == nptrs) { /* if pointer limit reached, realloc */
/* always realloc to temporary pointer, to validate success */
void *tmp = realloc (lines, sizeof *lines * nptrs * 2);
if (!tmp) { /* if realloc fails, bail with lines intact */
perror ("realloc - lines");
break; /* don't exit, lines holds current lines */
}
lines = tmp; /* assign reallocted block to lines */
/* zero all new memory (optional) */
memset (lines + nptrs, 0, nptrs * sizeof *lines);
nptrs *= 2; /* increment number of allocated pointers */
}
}
free (line); /* free memory allocated by getline */
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < ndx; i++) {
printf ("line[%3zu] : %s\n", i, lines[i]);
free (lines[i]); /* free memory for each line */
}
free (lines); /* free pointers */
return 0;
}
Look things over and let me know if you have further questions. If you do not have getline
or strdup
available, let me know and I'm happy to help further with an implementation that will provide their behavior.
来源:https://stackoverflow.com/questions/50778328/how-can-i-read-a-known-number-of-strings-of-unknown-size-from-a-txt-file-and-st