Function to read data from a file into heap memory - c

I am writing a function that reads the number of grades in a text file and stores them into heap memory. It writes the count to location pointed to by ptr_cnt and returns the address of the first byte stored into said heap memory.
I have written this and the count works just fine. However the data does not seem to be getting written into the the heap. When I print out the first heap, I get 0.000.
double *read_data(char const *file_name, int *ptr_cnt)
{
FILE *read = fopen(file_name, "r");
if (read == NULL)
{
return NULL;
}
int count = 0;
char ch;
while ((ch = fgetc(read)) != EOF)
{
if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\0')
{
count++;
}
}
*ptr_cnt = count;
double *heap;
heap = (double *)malloc(sizeof(double) * count);
double num = 0.0;
int i;
for (i = 0; fscanf(read, "%lf", &num) != EOF; i++)
{
*(heap + i) = num;
}
fclose(read);
return heap;
free(heap);
}

Related

How to parse bigger amount of words?

I have a program, which receives filename as an input, saves file contents into 2d char array and then outputs words. It works absolutely fine for about 400 words, but then, when I add more words, it crashes. Debugging showed that i am trying to access unused address, and I don't understand how is that possible considering that previous tests with lesser amount of words were successful.
The question is: what am i missing here?
FILE: functions.c
#include "Lab10.h"
#include <stdio.h>
#include <malloc.h>
char** parser(char* filename) {
FILE* fp;
fp = fopen(filename, "r");
char** str = (char**)calloc(N, sizeof(char*) * N);
if (!str)
{
printf("\n Allocation error");
return NULL;
}
char ch;
int space = 0, words = 0;
for (int i = 0; !feof(fp); i++) // Memory allocation
{
ch = fgetc(fp);
if (!is_ch(ch))
{
if (i != space)
{
if (!(str[words] = (char*)calloc(i - space, sizeof(char) * (i - space))))
{
printf("\n Allocation error");
return NULL;
}
words++;
}
while (!is_ch(ch) && !feof(fp))
{
ch = fgetc(fp);
i++;
}
if(!feof(fp))
fseek(fp, -(int)sizeof(char), 1);
i--;
space = i;
}
}
fseek(fp, 0, SEEK_SET);
for (int i = 0; i < words; i++) // Copying words into 2d array
{
while (!is_ch(fgetc(fp)));
if (!feof(fp))
fseek(fp, -(int)sizeof(char), 1);
int j = 0;
do {
if (((fscanf(fp, "%c", &str[i][j])) != 1))
break;
j++;
} while (is_ch(str[i][j-1]) && !feof(fp));
}
return str;
}
int is_ch(char ch)
{
return ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'));
}
FILE: main.c
#define _CRT_SECURE_NO_WARNINGS
#include "Lab10.h"
#include <stdio.h>
#include <stdlib.h>
int main() {
char* filename = (char*)malloc(sizeof(char) * N);
if (!scanf("%s", filename) || filename == 0)
{
printf("\n Incorrect filename input");
return -1;
}
char** str = parser(filename);
printf("\n Contents of .txt file:");
for (int i = 0; str[i] != NULL; i++) {
printf("\n\t%d) ", i+1);
for (int j = 0; is_ch(str[i][j]); j++) {
printf("%c", str[i][j]);
}
}
return 0;
}
This answer was posted as a reply to one of the comments below the question itself. I tried writing readWord function, which recieves filepointer, reads one word and then returns pointer to the resulting array - that's eases the procedure, making it less complex. It works almost like fgets(), but it reads till non-character, instead of a newline
readWord function itself:
char* readWord(FILE* fp) {
char ch = 0;
while (!is_ch(ch))
{
ch = fgetc(fp);
if (ch == EOF || !ch)
return NULL;
}
int size = 1;
while (is_ch(ch))
{
if ((ch = fgetc(fp)) == EOF || !ch)
break;
size++;
}
fseek(fp, -(size * (int)sizeof(char)), 1);
if (ch != EOF || !ch)
size--;
char* word = (char*)calloc(size, sizeof(char) * size + 1);
if (!word)
{
printf("\n Allocation error.");
return NULL;
}
for (int i = 0; i < size; i++)
word[i] = fgetc(fp);
word[size] = '\0';
return word;
}
That's how i use it in main():
FILE* fp = fopen("test.txt", "r");
char* word;
while ((word = readWord(fp)) != NULL)
{
for (int i = 0; word[i] != '\0'; i++)
printf("%c", word[i]);
printf(" ");
}
Is there is anything i need to improve here? It works fine, but is it possible to somehow make it better?

Passing a matrix using pointers in C

Compiles without warnings or errors, just the following code crashes when I try to either read from or write to the matrix constValues.
It needs to be passed to another function to be read from also; the function createOutputLine.
How can I point to the data held correctly so it can be modified and read from? Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
void createOutputFile(FILE*, int, char**);
char createOutputLine(int, int, char*, char**);
int main(int argc, char **argv) {
int removeComments = 0;
FILE *file;
if (argc > 1 && strcmp(argv[1], "-i") == 0) {
if (argc > 2) {
if (!(file = fopen(argv[2], "r"))) {
printf("Error: file not found");
return -1;
}
}
else {
printf("Error: no file specified");
return -1;
}
}
else {
printf("Error: command requires -i");
return -2;
}
createOutputFile(file, argc, argv);
fclose(file);
}
void createOutputFile(FILE *file, int argc, char **argv) {
fseek(file, 0, SEEK_SET);
char *data = (char*)malloc(2000);
FILE *header;
char name[20];
char *token = strtok(argv[2], ".");
strcpy(name, strcat(token, ".o"));
FILE *output = fopen(name, "w");
char constNames[10][15];
char **constValues[10][10];
int constsStored = 0;
while (fgets(data, 2000, file) != NULL) {
for (int i = 0; i < strlen(data); i++) {
int c = i;
bool linePrinted = false;
if (data[i] == '#' && data[i + 1] == 'd') {
for (c = i; c <= i + 7; c++) {
data[c] = '\0';
} int ch = 0;
while (data[c] != ' ') {
constNames[constsStored][ch] = data[c];
data[c] = '\0';
ch++;
c++;
} ch = 0;
while (data[c] != '\n') {
**constValues[constsStored][ch] = data[c]; //this line crashes
data[c] = '\0';
ch++;
c++;
}
if (data[c] == '\n') data[c] = '\0';
constsStored++;
}
for (int ch = 0; ch <= constsStored; ch++) {
if (data[i] == constNames[ch][0]) {
int ch2 = i + 1;
int ch3 = 1;
bool isConst = false;
while (data[ch2] != ' ') {
if (data[ch2] == constNames[ch][ch3] && isConst == false) isConst = true;
ch2++;
ch3++;
}
if (isConst || data[i + 1] == ' ') {
char line[200];
line[200] = createOutputLine(i, ch, data, **constValues);
fprintf(output, "%c", line[200]);
linePrinted = true;
}
}
}
if (!linePrinted)
fprintf(output, "%c", data[i]);
}
}
fclose(output);
free(data);
}
char createOutputLine(int i, int constElem, char *data, char **constValues) {
int ch = i;
int ch2 = 0;
char temp[200];
while (data[ch] != '\n' && data[ch] != ' ' && data[ch] != ';') {
temp[ch2] = data[ch];
printf("%c", data[ch]);
ch++;
ch2++;
}
char line[200];
ch2 = 0;
for (ch = i; ch <= sizeof(data); ch++) {
line[ch2] = data[ch];
ch2++;
}
for (ch = 0; ch <= 10; ch++) {
line[ch2] = constValues[constElem][ch];
ch2++;
}
for (ch = 0; ch <= sizeof(temp); ch++) {
line[ch2] = temp[ch];
ch2++;
}
line[ch2 + 1] = '\n';
return line[200];
}
A pointer shall point to an object before it can be derefenced. Full stop.
char **constValues[10][10]; just declares an 2D array of pointers to pointers to characters. And as it is an automatic array (neither statically nor dynamically allocated), its pointers are just uninitialized.
When you late use **constValues[constsStored][ch] = data[c];, you try to dereference an uninitialized pointer which is explicitely Undefined Behaviour. You are lucky to get an immediate crash, because UB consequences can be apparently unrelated problems.
The normal way is to declare arrays of objects, and use the addresses of those objects for pointers.
That's not all: C arrays are not first class citizens. You cannot assign to array, nor return it from a function. So this is plain wrong:
char line[200];
line[200] = createOutputLine(i, ch, data, **constValues);
It just assigns the unique character returned by the function past the end of the array!
So is this:
char line[200];
...
return line[200];
It does not return an array (C does not allow it) but the value of the byte that happens to live past the array.
I am sorry, but there are too many errors for me to fix them is such a long program.
You may find C hard and ask for help. But build small code containing only what you want to work on. And only when those small pieces work correctly, try to assemble them in a larger program.

Segmentation fault when appending char to string in string array

So I want to get all the lines from a file and turn them into a char* array. Problem is that whenever I try to append the character onto the end of the element it gives a segmentation fault.
char** loadOutputs(char *fileName, int *lineCount)
{
FILE *file = fopen(fileName, "r");
if (file) {
char c;
int lines = 0;
while ((c = fgetc(file)) != EOF)
if (c = '\n')
lines++;
rewind(file);
char **output = malloc(lines * sizeof(char*));
for (int i = 0; i < lines; i++)
output[i] = "";
int index = 0;
while ((c = fgetc(file)) != EOF)
if (c == '\n')
index++;
else
strcat(output[i], &c);
return output;
}
return NULL;
}
I always get a segmentation fault at strcat(output[i], &c);. I'd rather not create a fixed array size for the output because this could get fairly large and I don't want to use too much memory.
The following code:
for (int i = 0; i < lines; i++)
output[i] = "";
Is setting the pointer to an empty read only string.
You need to allocate some memory for the string:
I.e.
for (int i = 0; i < lines; i++) {
output[i] = malloc(MAX_LINE_LENGTH + 1);
}
Where MAX_LINE_LENGTH is some defined constant - perhaps #define MAX_LINE_LENGTH 100.
You will need to check that when reading the lines you do not exceed this length.
The following code will do this. This will resolve the other problem in that the address of c will not point to a null terminated string.
int index = 0;
int position = 0;
while ((c = fgetc(file)) != EOF) {
if (c == '\n') {
output[index][position] = 0; // Null terminate the line
position = 0; // Restart next line
index++;
} else {
if (position < MAX_LINE_LENGTH) { // Check if we have space!
output[index][position] = c; // Add character and move forward
position++;
}
}
}
output[index][position] = 0; // Add the null to the final line
Also you need to declare c as and int - i.e. change char c to int c. This is because EOF is outside the range for a char

Reading words of lines and storing words at 3D Array

I am dealing with such a problem that, I have one input file that contains words of lines, and I have wanted to store all words line by line with 3D-Array. But something seems wrong with my code, I think that I cannot see the point of where the segmentation fault is happening. Here is my code: For clarifications:
"char *input" is a filename |
"MAX_WORDS" is a macro that is equivalent to 500 |
"MAX_WLENGTH" is another macro that is equivalent to 64.
void func(char *input){
FILE *fp;
fp = fopen(input, "r");
char buffer[MAX_WORDS][MAX_WORDS][MAX_WLENGTH];
if(fp != NULL){
int c;
size_t i = 0;
size_t x = 0;
size_t y = 0;
for(;;){
c = fgetc(fp);
if(c != EOF && c != '\n' && c != ' '){
if(i < MAX_WLENGTH -1){
buffer[y][x][i++] = c;
}
continue;
}
if (i > 0){
buffer[y][x][i] = '\0';
if(c == '\n'){
y++;
x = 0;
i = 0;
}
else if(c == ' '){
x++;
i = 0;
}
}
if(c == EOF){
break;
}
}
fclose(fp);
}
int i;
int j;
for(i = 0; i < MAX_WORDS; ++i){
printf("\n");
for(j=0; j < MAX_WORDS; ++j){
printf("%s\t", strdup(buffer[i][j]));
}
}
}
You are not checking if your input file has more words than MAX_WORDS * MAX_WORDS (4096 words). So if your input file has more words then you are trying to access a location buffer[y][x][i] where y or x could be greater than 64 which might result in a segmentation fault. Use the following code instead of just y++; or x++;
y++;
if(y == MAX_WORDS){
break;
}

c freeing char pointer not working after first time

I am trying to free a char pointer wich was allocated by the function copyCharNumber. The first time the free call in the function works fine. The second time it doesn´t work any more and Visual Studio throws an error. I don`t get it why there is an error the second time. The second time calling free on the char pointer returned by the copyCharNumber anywhere in the code allways throws an error.
Code:
char* copyCharNumber(char *oldNumber, char *newDigit, int newLen){
char* newNumber = malloc(newLen * sizeof(char) +1);
if (oldNumber != NULL){
strcpy(newNumber, oldNumber);
free(oldNumber);
}
*(newNumber + (newLen - 1)) = *newDigit;
*(newNumber + newLen) = "\0";
return newNumber;
}
double* getNumbers(int max, int maxValue, int minValue, char* filenameRead){
double* numbers = malloc(max * sizeof(double));
FILE* fp = fopen(filenameRead, "r");
char ch = NULL;
int i = 0;
int numberLen = 0;
boolean isNegative = 0;
int hasPoint = 0;
for (; i < max && fp != NULL && ch != EOF; i++){
isNegative = 0;
numberLen = 0;
hasPoint = 0;
char* number = NULL;
if (ch == NULL)
ch = fgetc(fp);
if (ch == '-') isNegative = 1;
if (ch == '.' || ch == ',' && numberLen > 0) hasPoint++;
while (isdigit(ch)>0 || (isNegative && numberLen == 0) || (hasPoint == 1 && numberLen>0)){
numberLen++;
number = copyCharNumber(number, &ch, numberLen);
ch = fgetc(fp);
}
if (number != NULL){
double newNumber = strtod(number, NULL);
free(number);
if (newNumber <= maxValue && newNumber >= minValue){
if (isNegative) newNumber = -newNumber;
numbers[i] = newNumber;
}
}
else
ch = NULL;
}
return numbers;
}

Resources