Hi I was trying to create an array of string of an undetermined length in c.
This is my code :
int main()
{
int lineCount=linesCount();
char text[lineCount][10];
printf("%d",lineCount);
FILE * fpointer = fopen("test.txt","r");
fgets(text,10,fpointer);
fclose(fpointer);
printf("%s",text);
return 0;
}
I would like to replace 10 in
char text[lineCount][10];
My code reads out a file I already made the amount of lines dynamic.
Since the line length is unpredictable I would like to replace 10 by a something dynamic.
Thanks in advance.
To do this cleanly, we want a char * array rather than an 2D char array:
char *text[lineCount];
And, we need to use memory from the heap to store the individual lines.
Also, don't "hardwire" so called "magic" numbers like 10. Use an enum or #define (e.g) #define MAXWID 10. Note that with the solution below, we obviate the need for using the magic number at all.
Also, note the use of sizeof(buf) below instead of a magic number.
And, we want [separate] loops when reading and printing.
Anyway, here's the refactored code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
linesCount(void)
{
return 23;
}
int
main(void)
{
int lineCount = linesCount();
char *text[lineCount];
char buf[10000];
printf("%d", lineCount);
// open file and _check_ the return
const char *file = "test.txt";
FILE *fpointer = fopen(file, "r");
if (fpointer == NULL) {
perror(file);
exit(1);
}
int i = 0;
while (fgets(buf, sizeof(buf), fpointer) != NULL) {
// strip newline
buf[strcspn(buf,"\n")] = 0;
// store line -- we must allocate this
text[i++] = strdup(buf);
}
fclose(fpointer);
for (i = 0; i < lineCount; ++i)
printf("%s\n", text[i]);
return 0;
}
UPDATE:
The above code is derived from your original code. But, it assumes that the linesCount function can predict the number of lines. And, it doesn't check against overflow of the fixed length text array.
Here is a more generalized version that will allow an arbitrary number of lines with varying line lengths:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
int lineCount = 0;
char **text = NULL;
char buf[10000];
// open file and _check_ the return
const char *file = "test.txt";
FILE *fpointer = fopen(file, "r");
if (fpointer == NULL) {
perror(file);
exit(1);
}
int i = 0;
while (fgets(buf, sizeof(buf), fpointer) != NULL) {
// strip newline
buf[strcspn(buf,"\n")] = 0;
++lineCount;
// increase number of lines in array
text = realloc(text,sizeof(*text) * lineCount);
if (text == NULL) {
perror("realloc");
exit(1);
}
// store line -- we must allocate this
text[lineCount - 1] = strdup(buf);
}
fclose(fpointer);
// print the lines
for (i = 0; i < lineCount; ++i)
printf("%s\n", text[i]);
// more processing ...
// free the lines
for (i = 0; i < lineCount; ++i)
free(text[i]);
// free the list of lines
free(text);
return 0;
}
Related
This question already has answers here:
How should character arrays be used as strings?
(4 answers)
Closed 12 months ago.
I have a file with an unknown number of strings and each of these strings is of an unknown length.
I would like to make each line of the file its own string in an array of strings.
I tried to use dynamic allocation in a char** array, but I don't think I'm approaching this correctly.
Below is the code I have tried. It's getting stuck in an infinite loop, and I can't figure out why.
(The text file I'm reading from ends with a line break, by the way.)
#include <getopt.h> //for getopts
#include <sys/stat.h> //to do file stat
#include <dirent.h>
#include <string.h>
#include <pwd.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h> //user macros
#include <stdlib.h>
#include <stdbool.h>
#include <libgen.h>
#include <errno.h>
int main(int argc, char *argv[]) {
//storing the filename inside string
char* filename = argv[1];
FILE *fp1 = fopen(filename, "r");
if (fp1 == NULL) {
fprintf(stderr, "Error: Cannot open '%s'. No such file or directory.\n", filename);
return EXIT_FAILURE;
}
/**
* we begin by getting the number of numbers in the file
* the number of numbers = number of lines = number of line breaks
*/
size_t numNumbers = 0;
// while((fscanf(fp1, "%*[^\n]"), fscanf(fp1, "%*c")) != EOF){
// numNumbers = numNumbers + 1;
// }
char c;
while((c = fgetc(fp1)) != EOF){
if(c == '\n'){
numNumbers++;
}
}
fclose(fp1);
FILE *fp2 = fopen(filename, "r");
char** arrayOfStrings = malloc(numNumbers * sizeof(char*));
for(int i = 0; i < numNumbers; i++) {
int len = 0;
if(((c = fgetc(fp1)) != '\n') && (c != EOF)){
len++;
}
arrayOfStrings[i] = malloc(len * sizeof(char));
}
printf("hello1\n");
//for(int i = 0; i < numNumbers; i++){
// fscanf(fp2, "%s", (arrayOfStrings[i]));
//}
fclose(fp2);
// for(int i = 0; i < numNumbers; i++){
// fprintf(stdout, "%s", arrayOfStrings[i]);
// }
return 0;
}
(I'm very new to C, so please go easy on me!)
In C, strings are terminated with a '0' byte, so it looks like your malloc for each string is 1 character too short -- you've only allowed space for the text.
In addition, you mean the count for the size of each line to be a while loop, not an if statement - right now you are counting each line as length "1".
Finally, you are reading off the end of the file in your commented out fscanf code because you haven't closed and reopened it.
Assuming you want to split the input to the strings by the newline character, would you please try:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *filename; // filename to read
char **arrayOfStrings = NULL; // array of strings
char line[BUFSIZ]; // line buffer while reading
char *p; // temporal pointer to the input line
int i, num; // counter for lines
FILE *fp; // file pointer to read
if (argc != 2) {
fprintf(stderr, "usage: %s file.txt\n", argv[0]);
return EXIT_FAILURE;
}
filename = argv[1];
if (NULL == (fp = fopen(filename, "r"))) {
perror(filename);
return EXIT_FAILURE;
}
// read the input file line by line
while (fgets(line, BUFSIZ, fp)) {
if ((p = strrchr(line, '\n'))) *p = '\0'; // remove trailing newline, if any
if ((p = strrchr(line, '\r'))) *p = '\0'; // remove trailing cr character, if any
if (NULL == (arrayOfStrings = realloc(arrayOfStrings, (num + 1) * sizeof(char **)))) {
// enlarge the array according to the line count
perror("realloc");
return EXIT_FAILURE;
}
if (NULL == (arrayOfStrings[num] = malloc(strlen(line) + 1))) {
// memory for the string of the line
perror("malloc");
return EXIT_FAILURE;
}
strcpy(arrayOfStrings[num], line);
num++;
}
// print the strings in the array
for (i = 0; i < num; i++) {
printf("%d %s\n", i, arrayOfStrings[i]);
}
fclose(fp);
return 0;
}
If the input file looks something like:
This
is
the
input.
Then the output will be:
0 This
1 is
2 the
3 input.
I am trying to apply dynamic memory allocation on reading text files but I don't really get how I could access the contents of the file. I am still having difficulties understanding memory allocation so if it is possible, please explain how I can apply it on file handling.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
int counter = 0;
char ch;
char **chpt;
FILE *fp;
fp = fopen("test.txt", "r");
while((ch = fgetc(fp)) != EOF ){
counter++;
}
rewind(fp);
chpt = (char **)malloc(counter * sizeof(char));
fread(chpt, counter * sizeof(char), 1, fp);
for (int i = 0; i < counter; i++){
for (int j = 0; j < counter; j++) {
printf("%c", chpt[i][j]);
}
}
fclose(fp);
free(chpt);
return 0;
}
Your nested for loops don't make any sense and you're trying to print counter*counter characters, but you have only read counter characters. You don't have a 2D array here, and you don't need one either.
Furthermore:
you need to check if fopen fails
the cast with malloc is not needed (but it doesn't harm either)
Your file contains obviously counter characters. So you need to allocate memory for counter characters, read counter characters from the file, and then display the counter characters you've just read.
You want this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
int counter = 0;
char ch;
char* chpt; // just a pointer to char
FILE* fp;
fp = fopen("test.txt", "r");
if (f == NULL) // check if fopen failed
{
printf("Can't open file\n"); // print error message
return 1; // and abort
}
while ((ch = fgetc(fp)) != EOF) {
counter++;
}
rewind(fp);
chpt = malloc(counter * sizeof(char)); // no cast needed
fread(chpt, counter * sizeof(char), 1, fp);
for (int i = 0; i < counter; i++) { // one loop is enough
printf("%c", chpt[i]);
}
fclose(fp);
free(chpt);
return 0;
}
There is still room for further improvement:
you should check if malloc fails, even if it's unlikely to fail if the file isn't huge
your method of determining the file size is very inefficient, for more information read this
sizeof(char) is not needed, it is 1 by definition.
again. I'm new to C. Still thinking in Python terms (readlines, append them to variable) so I'm having difficulties translating that to C. This is what I want to do: open a text file for reading, store each line in an array row by row, print it out to make sure it's stored.
This is how far I've got:
int main(){
FILE * fp = fopen("sometext.txt", "r");
char text[100][100];
if(fp == NULL){
printf("File not found!");
}
else{
char aLine[20];
int row = 0;
while(fgets(aLine, 20, fp) != NULL){
printf("%s", aLine);
//strcpy(text[row], aLine); Trying to append a line (as row)
return 0;
}
Please don't start with "invest in some more time and look somewhere else because it's easy and has been answered". I'm bad at this, and I'm trying.
You can try this. Basically you need an array of arrays to store each line. You find the length of the longest line in the file and you allocate space for it. Then rewind the pointer to the start of the file and use fgets to get each line from the file and strdup to allocate space and copy the line to the respective position. Hope this helps.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
FILE * fp = fopen("sometext.txt", "r");
int maxLineSize = 0, count = 0;
char c;
while ((c = fgetc(fp)) != EOF) {
if (c == '\n' && count > maxLineSize) maxLineSize = count;
if (c == '\n') count = 0;
count++;
}
rewind(fp);
char ** lines = NULL;
char * line = calloc(maxLineSize, sizeof(char));
for (int i = 0 ; fgets(line, maxLineSize + 1, fp) != NULL ; i++) { // +1 for \0
lines = realloc(lines, (i + 1) * sizeof(char *));
line[strcspn(line, "\n")] = 0; // optional if you want to cut \n from the end of the line
lines[i] = strdup(line);
printf("%s\n", lines[i]);
memset(line, maxLineSize, '\0');
}
fclose(fp);
}
You could solve it without copy
The follow code could work:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <math.h>
int main()
{
FILE * fp = fopen("sometext.txt", "r");
if(fp == NULL){
printf("File not found!");
return -1;
}
char text[100][20];
int row = 0;
while(row < 100 && fgets(text[row], sizeof(text[0]), fp) != NULL)
++row;
for (int i= 0; i != row; ++i)
fputs(text[i], stdout);
return 0;
}
If I'm printing the whole string everything looks good, whitespace and indenting looks perfect (I'm loading the source file with this code).
But if I'm trying to print a single character in the buffer I'm getting letters where there are not supposed to be any.
For example, if I print buffer[2] I'm getting letters where it should be whitespace, but if I print the whole string the letters aren't there.
Here is my code that's not working:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char *buffer = (char*) malloc(100*sizeof(char));
FILE *myFile;
myFile = fopen("thisSourceFile.c", "r");
if (!myFile) {
printf("could not open file");
}
else {
while(fgets(buffer,100,myFile)) {
printf("%c \n",buffer[2]);
}
}
fclose(myFile);
free(buffer);
buffer = NULL;
return 0;
}
OUTPUT:
n
n
n
t
h
I
y
f
p
l
w
p
}
r
u
e
As you can se it is printing letters where it should by whitespace. Those letters are not there if I print the whole string.
If you're interested in parsing a source file and processing each character, this might be a solution.
But there are two constants; charsand num_lines_to_read.
M.M mentions in the comments below that isprint() isn't fully portable and comes with some quirks to be careful of.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(void) {
const int chars = 100; /* Num chars per line to read */
const int num_lines_to_read = 3; /* Num lines to read */
char *buffer = (char*) malloc(chars*sizeof(char));
int i = 0, j = 0;
FILE *myFile;
myFile = fopen("thisSourceFile.c", "r");
if (myFile == NULL) {
printf("could not open file");
fclose(myFile);
return 1;
}
for(i=0; i<num_lines_to_read; i++)
{
if(fgets(buffer,chars,myFile) != NULL)
{
while(isprint((unsigned char) buffer[j]))
{
printf("%c", (buffer[j]));
j++;
}
j=0;
}
}
fclose(myFile);
free(buffer);
return 0;
}
Example output (itself!):
#include <stdio.h>#include <stdlib.h>#include <ctype.h>
I'm quite new to C programming and have just begun studying files. I'm wondering whether it is possible to read a file line by line (including spaces in each line) into an array of size equal to the number of lines in the file. I really have no idea where to start or whether this is even possible so any guidance at all would be much appreciated.
Example
A text file in the form of:
Computer Programming
Software Engineering
Computer Architecture
to be written into array such that:
char array[4];
array[0] = "Computer Programming";
array[1] = "Software Engineering";
array[2] = "Computer Architecture";
All I have so far is:
int main()
{
char array[50];
bool answer;
FILE *classes;
classes = fopen("classnames.txt", "r");
if(classes == NULL){
printf("\n ************* ERROR *************\n");
printf("\n \"classnames.txt\" cannot be opened.\n");
printf("\n PROGRAM TERMINATED\n");
exit(EXIT_FAILURE);
}
And next I would like to write each class name into each element of the array.
Yes, you just have to declare array as char** and dynamically allocate as you read each line. E.g.
int MAX_NUM_LINES = 1000;
int MAX_LINE_LEN = 256;
char** array;
malloc(array, MAX_NUM_LINES*sizeof(char*));
fp = fopen(...);
int line_ct = 0;
char line[MAX_LINE_LEN];
while ( fgets(line, MAX_LINE_LEN, fp) != NULL )
{
int len = strlen(line);
malloc(array[line_ct], len * sizeof(char));
strcpy(array[line_ct], line);
line_ct++;
}
I have not actually tried to compile this code, but something like this will work. You can also replace MAX_NUM_LINES with the actual value by doing a quick runthrough first and counting the lines--that would be preferable probably.
This is an example of a possible approach
#include <stdio.h>
#include <string.h>
int main()
{
char array[100][100];
char line[100];
size_t arraySize;
size_t count;
FILE *file;
const char *filepath;
filepath = "<put the file path here>";
file = fopen(filepath, "r");
if (file == NULL)
{
perror("fopen()");
return -1;
}
count = 0;
arraySize = sizeof(array) / sizeof(array[0]);
while ((fgets(line, sizeof(line), file) != NULL) && (count < arraySize))
{
size_t length;
length = strlen(line);
if (line[length] == '\0')
line[--length] = '\0';
memcpy(array[count++], line, 1 + length);
}
fclose(file);
return 0;
}