fgets + dynamic malloc/realloc with a .txt as stdin - c

i'm trying to read lines of a file. txt, but without knowing the size of each lines...First I used the getline instruction (and works fine), but my teacher does not let me use that instruction, he says I can only use the fgets statement with malloc and realloc...
This is an input example, with variable line sizes:
[9.3,1.2,87.9]
[1.0,1.0]
[0.0,0.0,1.0]
As shown, each line defines a different vector with no size limit
Someone could help me implement this method?
Thank you very much.
NOTE: I forgot to mention, to compile the program I use these commands:
g++ -Wall-Wextra-Werror-pedantic main.c-o metbasicos.c metintermedios.c eda.exe
./eda.exe <eda.txt

I would say do something similar to this
while(fgets(buf, LEN, stdin)){
z = strtok(buf, ",");
*(*(matrix + i)) = atof(z);
for(j = 1; j < col; ++j){
z = strtok(NULL, ",");
*(*(matrix + i) + j) = atof(z);
}
++i;
}
The only extra thing you would have to take care of is making sure that you strip the brackets off of the first and last element.
Of course, if you don't know the size of the final array, you might need something like this:
struct data_t {
int nval; /* current number of values in array */
int max; /* allocated number of vlaues */
char **words; /* the data array */
};
enum {INIT = 1, GROW = 2};
...
while (fgets(buf, LEN, stdin)) {
if (data->words == NULL)
data->words = malloc(sizeof(char *));
else if (data->nval > data->max) {
data->words = realloc(data->words, GROW * data->max *sizeof(char *));
data->max = GROW * data->max;
}
z = strtok(buf, "\n");
*(data->words + i) = malloc(sizeof(char) * (strlen(z) + 1));
strcpy(*(data->words + i), z);
i++;
data->nval++;
}
data->nval--;
If you combine both of those while loops into a single one, you should be all set. The first one reads in floats, the second one is good for dynamically allocating space on the fly.

If you can use multiple steps, then use one function to get the information you need to malloc memory. (for example determine number of lines, and longest line) This function will do that for you (given the file name and location)
[EDIT] LineCount - This function will get you the number of lines, and the longest line so you can dynamically allocate memory in char **strings; in which to read the lines of the input file.
int lineCount(char *file, int *nLines)
{
FILE *fp;
int cnt=0, longest=0, numLines=0;
char c;
fp = fopen(file, "r");
while ( (c = fgetc ( fp) ) != EOF )
{
if ( c != '\n' )
{
cnt++;
if (cnt > longest) longest = cnt;
}
else
{
numLines++;
cnt= 0;
}
}
*nLines = numLines+1;//add one more
fclose(fp);
return longest+1;
}
Here is the implementation to read the input file you provided, using the function above to get the unknown dimensions of the input file...
#include <ansi_c.h>
#include <stdio.h>
#define FILENAME "c:\\dev\\play\\in.txt" //put your own path here
#define DELIM "- ,:;//_*&[]\n" //change this line as needed for search criteria
int lineCount(char *file, int *cnt);
void allocMemory(int numStrings, int max);
void freeMemory(int numStrings);
char **strings;
int main()
{
int numLines, longest, cnt, i;
FILE *fp;
longest = lineCount(FILENAME, &numLines);
char wordKeep[longest];
allocMemory(numLines, longest);
//read file into string arrays
fp = fopen(FILENAME, "r");
cnt=0;
i=0;
for(i=0;i<numLines;i++)
{
fgets(strings[i], longest, fp);
}
fclose(fp);
freeMemory(numLines);
getchar();
return 0;
}
int lineCount(char *file, int *nLines)
{
FILE *fp;
int cnt=0, longest=0, numLines=0;
char c;
fp = fopen(file, "r");
while ( (c = fgetc ( fp) ) != EOF )
{
if ( c != '\n' )
{
cnt++;
if (cnt > longest) longest = cnt;
}
else
{
numLines++;
cnt= 0;
}
}
*nLines = numLines+1;//add one more
fclose(fp);
return longest+1;
}
void allocMemory(int numStrings, int max)
{
int i;
// need number of lines by longest line for string containers
strings = calloc(sizeof(char*)*(numStrings+1), sizeof(char*));
for(i=0;i<numStrings; i++)
{
strings[i] = calloc(sizeof(char)*max + 1, sizeof(char));
}
}
void freeMemory(int numStrings)
{
int i;
for(i=0;i<numStrings; i++)
if(strings[i]) free(strings[i]);
free(strings);
}

Related

How can I alphabetize the first column of a .csv file in C?

I have a .csv file. Let's say the data is like this:
Location 1,Location 2,Price,Rooms,Bathrooms,CarParks,Type,Area,Furnish
Upper-East-Side,New-York,310000,3,2,0,Built-up,1000,Partly
West-Village,New-York,278000,2,2,0,Built-up,1000,Partly
Theater-District,New-York,688000,3,2,0,Built-up,1000,Partly
Expected output (alphabetized):
Theater-District
Upper-East-Side
West-Village
How can I only show and alphabetize the first column (Location 1) of the file while also skipping the header?
This is currently my code but it's still in a "read and display" form.
#include <stdio.h>
int main()
{
FILE *fh;
fh = fopen("file.csv", "r");
if (fh != NULL)
{
int line_number = 0;
char c;
while ( (c = fgetc(fh)) != EOF )
{
if(line_number > 0 || c == '\n'){
putchar(c);
}
if(c == '\n'){
line_number++;
}
}
fclose(fh);
} else printf("Error opening file.\n");
return 0;
}
csv is not a well defined format so I suggest you use an existing csv library instead of parsing the data yourself. For instance, this will not work if the first field has any embedded commas. It relies on scanf() to allocate the line, and resizes the lines array as needed. This means there are no arbitrary limits.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int strcmp2(const void *a, const void *b) {
return strcmp((const char *) a, (const char *) b);
}
int main() {
FILE *f = fopen("unsorted.csv", "r");
if(!f) return 1;
char **lines = NULL;
size_t n = 0;
for(;; n++) {
char *location1;
int rv = fscanf(f, "%m[^,]%*[^\n]\n", &location1);
if(rv != 1) break;
char **tmp = realloc(lines, (n + 1) * sizeof *tmp);
if(!tmp) return 1;
lines = tmp;
tmp[n] = location1;
}
fclose(f);
free(lines[0]); // header
qsort(&lines[1], n - 1, sizeof *lines, strcmp2);
for(size_t i = 1; i < n; i++) {
printf("%s\n", lines[i]);
free(lines[i]);
}
free(lines);
}
It produces the expected output:
Theater-District
Upper-East-Side
West-Village
So, assuming some hard limits on line length and CSV file record count, we can just use arrays.
To read a record, just use fgets(). Add each line of text to the array using the usual method.
We use a simple string search and truncate to isolate the first field. (Assuming no fancy stuff like double-quoted fields. I assume you are doing homework.)
To sort everything except the CSV header record, use qsort() with a little additional mathematics.
#include <iso646.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define unused(x) (void)(x)
#define MAX_LINE_LENGTH 100
#define MAX_RECORD_COUNT 100
int main( int argc, char ** argv )
{
unused( argc );
char records[MAX_RECORD_COUNT][MAX_LINE_LENGTH];
size_t record_count = 0;
const char * filename = argv[1];
if (!filename) return 1;
// Read our records from file
FILE * f = fopen( filename, "r" );
if (!f) return 1;
while ((record_count < MAX_RECORD_COUNT)
and fgets( records[record_count], MAX_LINE_LENGTH, f ))
record_count += 1;
fclose( f );
// Truncate the strings to just the first field
for (size_t n = 0; n < record_count; n++)
{
char * p = strchr( records[n], ',' );
if (p) *p = '\0';
}
// Sort everything but the header
if (record_count > 2) // must exist at least two records + header
qsort( records+1, record_count-1, MAX_LINE_LENGTH,
(int (*)( const void *, const void * ))strcmp );
// Print everything but the header
for (size_t n = 1; n < record_count; n++)
printf( "%s\n", records[n] );
return 0;
}

Creating 1000 text files in C

I am learning C language. Here is a simple program I did to create 1000 text files.
#include <stdio.h>
#include <string.h>
char * create_filename(char *, char *, int);
int main(void)
{
char prefix_name[50] = "file_no_";
char snum[5];
int no_of_files = 1000;
FILE * fp = NULL;
for (int i = 0; i < no_of_files; i++)
{
fp = fopen( create_filename(prefix_name, snum, i + 1), "w");
fprintf(fp, "This is file no %d", i+1);
fclose(fp);
fp = NULL;
strcpy(prefix_name, "file_no_");
}
return 0;
}
char * create_filename(char * prefix_name, char * snum, int i)
{
sprintf(snum, "%d", i);
strcat(prefix_name, snum);
strcat(prefix_name, ".txt");
return prefix_name;
}
This runs as expected. But I want to know, how can I make this more efficient and as portable as possible. If I want to scale this up to, say 10000 text files, are there other approaches which will be better ?
Thanks
how can I make this more efficient and as portable as possible.
More error checking. Example: a failed fopen() can readily occur.
Realize that a huge amount of time will occur in fopen() and local code likely will have scant time improvements.
Avoid re-writing the prefix.
Use a helper function.
Example:
// Return count of successfully written files.
int create_many_files(const char *prefix, int count, const char *suffix) {
int n;
int len = snprintf(NULL, 0, "%s%n%d%s", prefix, &n, count, suffix);
if (len < 0) {
return 0;
}
// Use len to determine longest name and use a VLA or allocation.
// Consider using a fixed array when len is not too big.
char *filename = malloc((size_t)len + 1u);
if (filename == NULL) {
return 0;
}
strcpy(filename, prefix);
char *offset = filename + n;
for (int i = 0; i < count; i++) {
sprintf(offset, "%d%s", i + 1, suffix);
FILE *fp = fopen(filename, "w");
if (fp == NULL) {
free(filename);
return i;
}
fprintf(fp, "This is file no %d", i + 1);
fclose(fp);
}
free(filename);
return count;
}
Other potential error checks:
prefix == NULL
cout < 0
suffix == NULL
fprintf() < 0
fclose() != 0

How to implement MPI in my C program to read file & remove space from it

I am new to C, After 4 days, I finally managed to make a program that read a file and remove space from it. I need to also make it parallel using MPI in any way. I tried various solutions, but MPI does not seem straightforward, it is complex, can someone please help me a bit to move forward.
Here is my code. It first reads a text file, and then removes space and new line characters.
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
FILE* pInputFile;
int chr = 0;
int main()
{
FILE* fptr;
char c;
char filename[] = "Lorem.txt";
char* str, * strblank;
int i = 0;
errno_t err;
if ((err = fopen_s(&pInputFile, filename, "r")) == 0)
{
/*count the number of characters in file for file initialization*/
size_t pos = ftell(pInputFile); // Current position
fseek(pInputFile, 0, SEEK_END); // Go to end
size_t length = ftell(pInputFile); // read the position which is the size
fseek(pInputFile, pos, SEEK_SET); // restore original position
//creating dynamic array of file size
str = malloc(length * sizeof(char));
strblank = malloc(length * sizeof(char));
while ((chr = getc(pInputFile)) != EOF)
{
str[i] = chr;
i++;
}
i = 0;
printf("%s", str);
removespace(str, strblank);
printf("%s", strblank);
fclose(pInputFile);
}
else
{
fprintf(stderr, "Cannot open file, error %d\n", err);
}
return 0;
}
int removespace(char aj[500], char mj[500])
{
int i = 0, j = 0, len;
len = strlen(aj); // len stores the length of the input string
while (aj[i] != '\0') // till string doesn't terminate
{
if (aj[i] != ' ' && aj[i] != '\n') // if the char is not a white space
{
/*
incrementing index j only when
the char is not space
*/
mj[j++] = aj[i];
}
/*
i is the index of the actual string and
is incremented irrespective of the spaces
*/
i++;
}
mj[j] = '\0';
printf("\n\nThe string after removing all the spaces is: ");
return 0;
}

C programming: lines of text file to integer array

I want to change my input.txt file to an integer array.
But sadly I keep missing one integer whenever new-line-character is met.
Following is my main()
int main(int args, char* argv[]) {
int *val;
char *STRING = readFile();
val = convert(STRING);
return 0;
}
Following is my file input function
char *readFile() {
int count;
FILE *fp;
fp = fopen("input.txt", "r");
if(fp==NULL) printf("File is NULL!n");
char* STRING;
char oneLine[255];
STRING = (char*)malloc(255);
assert(STRING!=NULL);
while(1){
fgets(oneLine, 255, fp);
count += strlen(oneLine);
STRING = (char*)realloc(STRING, count+1);
strcat(STRING, oneLine);
if(feof(fp)) break;
}
fclose(fp);
return STRING;
}
Following is my integer array function
int *convert(char *STRING){
int *intarr;
intarr = (int*)malloc(sizeof(int)*16);
int a=0;
char *ptr = strtok(STRING, " ");
while (ptr != NULL){
intarr[a] = atoi(ptr);
printf("number = %s\tindex = %d\n", ptr, a);
a++;
ptr = strtok(NULL, " ");
}
return intarr;
}
There are many issues.
This is a corrected version of your program, all comments are mine. Minimal error checking is done for brevity. intarr = malloc(sizeof(int) * 16); will be a problem if there are more than 16 numbers in the file, this should be handled somehow, for example by growing intarr with realloc, similar to what you're doing in readFile.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
char *readFile() {
FILE *fp;
fp = fopen("input.txt", "r");
if (fp == NULL)
{
printf("File is NULL!n");
return NULL; // abort if file could not be opened
}
#define MAXLINELENGTH 255 // define a constant rather than hardcoding "255" at several places
char* STRING;
char oneLine[MAXLINELENGTH];
STRING = malloc(MAXLINELENGTH);
int count = MAXLINELENGTH; // count mus be initialized and better declare it here
assert(STRING != NULL);
STRING[0] = 0; // memory pointed by STRING must be initialized
while (fgets(oneLine, MAXLINELENGTH, fp) != NULL) // correct usage of fgets
{
count += strlen(oneLine);
STRING = realloc(STRING, count + 1);
strcat(STRING, oneLine);
}
fclose(fp);
return STRING;
}
int *convert(char *STRING, int *nbofvalues) { // nbofvalues for returning the number of values
int *intarr;
intarr = malloc(sizeof(int) * 16);
int a = 0;
char *ptr = strtok(STRING, " \n"); // strings may be separated by '\n', or ' '
*nbofvalues = 0;
while (ptr != NULL) {
intarr[a] = atoi(ptr);
printf("number = %s\tindex = %d\n", ptr, a);
a++;
ptr = strtok(NULL, " \n"); // strings are separated by '\n' or ' '
} // read the fgets documentation which
// terminates read strings by \n
*nbofvalues = a; // return number of values
return intarr;
}
int main(int args, char* argv[]) {
int *val;
char *STRING = readFile();
if (STRING == NULL)
{
printf("readFile() problem\n"); // abort if file could not be read
return 1;
}
int nbvalues;
val = convert(STRING, &nbvalues); // nbvalues contains the number of values
// print numbers
for (int i = 0; i < nbvalues; i++)
{
printf("%d: %d\n", i, val[i]);
}
free(val); // free memory
free(STRING); // free memory
return 0;
}
I'm not sure what your requirement is, but this can be simplified a lot because there is no need to read the file into memory and then convert the strings into number. You could convert the numbers on the fly as you read them. And as already mentioned in a comment, calling realloc for each line is inefficient. There is room for more improvements.

Assigning each line of a file into each element of an array

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;
}

Resources