Load File Into Char* Array - c

Feel silly asking this question, since this should be easy, but I can't figure out whats wrong.
void loadIniIntoMemory() {
FILE *fp ;
fp = fopen (iniFile, "r");
int ch;
int final_line_num = 0;
int char_index;
char* current_line = (char*) malloc(sizeof(char) * MAX_INI_LINE_LENGTH);
while((ch = fgetc(fp)) != EOF) {
if(ch == 10) {
// new line
*(current_line + char_index) = '\0';
char_index = 0;
iniFileData[final_line_num] = current_line;
final_line_num++;
} else {
// regular char
*(current_line + char_index) = ch; // CAN'T DO THIS, CRASH
char_index++;
if(ch == 13) {
// carriage return
continue;
}
}
}
}
Been a little while since I did C, it crashes at this line : *(current_line + char_index) = ch;
Thanks for any help.
--EDIT--
Also, no one noticed, that this code doesn't save the last line. Here is the full, correct, working code which saves a file into an array of pointers.
void loadIniIntoMemory() {
FILE *fp ;
fp = fopen (iniFile, "r");
int ch;
final_line_num = 0;
int char_index = 0;
char* current_line = (char*) malloc(sizeof(char) * MAX_INI_LINE_LENGTH);
while((ch = fgetc(fp)) != EOF) {
if(ch == '\n') {
// new line
*(current_line + char_index) = '\0';
char_index = 0;
iniFileData[final_line_num] = current_line;
final_line_num++;
current_line = (char*) malloc(sizeof(char) * MAX_INI_LINE_LENGTH);
} else if(ch != '\r') {
// regular char
*(current_line + char_index) = ch;
char_index++;
}
}
iniFileData[final_line_num] = current_line;
fclose(fp);
}

For starters, you don't initialize char_index, meaning it will likely have garbage in it. If you don't initialize it, your program will add some unknown number to the current_line pointer.
int char_index = 0; /* initialize to 0 */
Secondly, a bit more "natural" syntax would be:
current_line[char_index] = ...
Thirdly, you can test the characters without using their integer equivalents:
if (ch == '\n') {
/* this is the same as "ch == 10" */
Fourth, you should close the open file prior to leaving the routine:
fclose(fp);
Finally, I'm not sure what the ch == 13 ('\r') and continue is meant to handle, since the continue is effectively a no-op, but you probably don't want to copy it into the data:
if (ch != '\r') {
current_line[char_index] = ch;
char_index++;
/* or on one line: current_line[char_index++] = ch; */
}
As an aside, a powerful feature of C (and many other languages) is the switch statement:
/* substitutes your if...elseif...else */
switch (ch) {
case '\n':
current_line[char_index] = '\0';
char_index = 0;
iniFileData[final_line_num++] = current_line;
break; /* <-- very important, C allows switch cases to fall thru */
case '\r':
/* do nothing */
break;
default:
/* any character that is not a newline or linefeed */
current_line[char_index++] = ch;
break;
}

You didn't initialize char_index. I suppose you want to initialize it to 0. In C, uninitialized variable will contain garbage. Most likely your char_index equals some very large number.

Besides what others have already pointed out, you will also have to move the line buffer allocation call into the loop and immediately after the final_line_num++; statement. Otherwise, each new line you read will be overwriting the previous line.

And some operating systems, like most POSIX compliant ones, in particular Linux, gives you the ability to map a file segment (possibly the entire file) into virtual memory. On Linux, you could consider using the mmap system call

Related

I want to read a certain part of a line in C

I want to read a specific part of a line. I don't want to add anything after "// " as shown in the example. I've defined a flag for it. But I'm having a hard time getting to the bottom line. How can I solve this ?
void read_file(char *filename) {
int flag = 0;
char line[MAX_LINE_LENGTH];
FILE *f = fopen(filename, "r");
while(fscanf(f, "%s", line) != EOF) {
if(!strcmp(line, "//")) {
flag = 1;
}
if(flag == 0) {
printf("%s", line);
}
}
}
Your fscanf is wrong. It won't return EOF. You want to compare against != 1 instead.
But, if you want to strip the comment, and just print the [preceding] data, you'll want strstr instead of strcmp
Also, I'd use fgets instead of fscanf ...
Here's your code refactored:
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 1000
void
read_file(char *filename)
{
int flag = 0;
char *cp;
char line[MAX_LINE_LENGTH];
FILE *f = fopen(filename, "r");
while (1) {
cp = fgets(line,sizeof(line),f);
if (cp == NULL)
break;
cp = strstr(line,"//");
if (cp != NULL) {
*cp++ = '\n';
*cp = 0;
}
printf("%s", line);
}
}
You may want to clean this up a bit more to do better handling of the newline [which fgets retains].
UPDATE:
Yes, my code is fixed. Thank you so much. But I don't understand here : *c = 0
A string in C is a char array that just "happens" to have a zero byte as the string terminator.
fgets will guarantee that the buffer has a trailing 0
Likewise, printf expects line to be 0 terminated.
We're trying to clip out the // and everything else on the line that follows it.
It's not enough to just add back a newline there. We have to add [back] the 0 at the now shortened string as the new place for the string terminator.
If the original line was:
foo // bar<newline><0x00>
We want:
foo <newline><0x00>
If you want to see why, just comment out the *cp = 0;. You'll get "interesting" results ...
UPDATE #2:
In order to not change your code so much, I left the newline in the string.
But, normally, I always strip the newline out before doing any processing. I find that to be a bit cleaner [even if a bit slower].
Here's a version that does that and is closer to what I would have written from scratch:
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 1000
void
read_file(char *filename)
{
int flag = 0;
char *cp;
char line[MAX_LINE_LENGTH];
FILE *f = fopen(filename, "r");
while (1) {
cp = fgets(line,sizeof(line),f);
if (cp == NULL)
break;
// strip newline
cp = strchr(line,'\n');
if (cp != NULL)
*cp = 0;
// find [and strip] the comment
cp = strstr(line,"//");
if (cp != NULL)
*cp = 0;
printf("%s\n", line);
}
}
Note that because the newline has been pre-stripped [if it exists on the line], the printf has to be modified to add one when printing.
UPDATE #3:
Using strchr and strstr are the easy thing to do. But, this requires that line be scanned twice, which is a bit wasteful.
The str* functions in libc are a convenience, but we can write a custom function that does a single pass on the line and strips either the comment or the newline whichever comes first.
This may help explain what a "string" in C really is, since we're doing "all the magic" so to speak:
#include <stdio.h>
#include <string.h>
#define MAX_LINE_LENGTH 1000
void
fixline(char *line)
{
int chr;
for (chr = *line++; chr != 0; chr = *line++) {
// strip newline
if (chr == '\n') {
line[-1] = 0;
break;
}
// wait for first "/"
if (chr != '/')
continue;
// peek at next char (is it '/' -- if so, we have "//")
chr = *line;
if (chr == '/') {
line[-1] = 0;
break;
}
}
}
void
read_file(char *filename)
{
int flag = 0;
char *cp;
char line[MAX_LINE_LENGTH];
FILE *f = fopen(filename, "r");
while (1) {
cp = fgets(line,sizeof(line),f);
if (cp == NULL)
break;
// strip newline and/or comment
fixline(line);
printf("%s\n", line);
}
}
UPDATE #4:
Thank you I got it. But when I try it this way, why can't I get the right result? if(!strcmp("data", line)) { printf("helloo"); } break; When you add this part to the end of the code, it doesn't work correctly.
If you've added this after the newline strip, it should work, because the data line has nothing else on it and it starts in the first position of the buffer.
But, indented this is:
if (!strcmp("data", line)) {
printf("helloo");
}
break;
You probably want:
if (!strcmp("data", line)) {
printf("helloo");
break;
}
Both strchr and strstr scan the entire string looking for a matching character [strchr]. Or, the start of matching substring [strstr].
strcmp does not scan the string in that sense. It just loops through both strings, char-by-char and stops on EOS. It just checks for equal strings, both starting at the first char of each array.
Real strcmp will use special instructions to make it very fast [as will other libc functions], but here is what strcmp actually does:
int
strcmp(const char *s1,const char *s2)
{
int c1;
int c2;
int cmp = 0;
while (1) {
c1 = *s1++;
c2 = *s2++;
if (c1 < c2) {
cmp = -1;
break;
}
if (c1 > c2) {
cmp = +1;
break;
}
// end of string -- both strings equal
if (c1 == 0)
break;
}
return cmp;
}
Format %s reads words (i.e. everything until the next space, tab, newline, ...) but not lines; That's probably why you have a hard time.
For reading in complete lines (i.e. everything up to a new line), use function fgets;
For cutting the line off at a "//", use strstr as follows:
char *beginOfComment = strstr(line,"//");
if (beginOfComment) {
*beginOfComment = 0x0;
}
#Craig Estey
while (1)
{
cp = fgets(line, sizeof(line), f);
if (cp == NULL)
break;
if(!strcmp("actions", cp)) {
break;
}
cp = strstr(line, "//");
if (cp != NULL)
{
*cp++ = '\n';
*cp = 0;
}
printf("%s", line);
}
Why is the strcmp function not working in this way?

Reading in a line from file or stdin dynamically

I am posed with a situation where my function does exactly what I want except handle higher amounts of input.
I initially thought to process each character one by one but was running into problems doing this. So fscanf not only does what I want it to do but it is essential in reading in only one line. I noticed, I cannot reallocate space for bigger array this way though. I have tried using format specifiers i.e. %*s to include a specific amount of buffer space before hand but this still does not work.
I have noticed also, I would have no way of knowing the size of the string I am reading in.
Here is my attempt and thoughts:
#define LINE_MAX 1000
char* getline(FILE* inputStream)
{
int capacity = LINE_MAX;
char* line = malloc(capacity * sizeof(char));
int ch;
/* if (sizeof(capacity) == sizeof(line)) { // Not a valid comparison? Too late?
capacity *= 2;
line = realloc(line, capacity * sizeof(line));
} */
if (fscanf(stream, "%[^\n]s", line) == 1) {
ch = fgetc(inputStream);
if (ch != '\n' && ch != EOF) {
fscanf(inputStream, "%*[^\n]");
fscanf(inputStream, "%*c");
}
free(line);
return line;
}
free(line);
return NULL;
}
I am new to memory allocation in general but I feel as though I had a good idea of what to do here. Turns out I was wrong.
Here is an example to read a line and store it in a Character array.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
signed char *str;
int c;
int i;
int size = 10;
str = malloc(size*sizeof(char));
for(i=0;(c=getchar()) !='\n' && c != EOF;++i){
if( i == size){
size = 2*size;
str = realloc(str, size*sizeof(char));
if(str == NULL){
printf("Error Unable to Grow String! :(");
exit(-1);
}
}
str[i] = c;
}
if(i == size){
str = realloc(str, (size+1)*sizeof(char));
if(str == NULL){
printf("Error Unable to Grow String! :(");
exit(-1);
}
}
str[i] = '\0';
printf("My String : %s", str);
return 0;
}
The array is resized to twice it's original size if current array can't hold the characters read from input.

Having trouble checking for newline when reading file

I'm trying to write a program to read a file of an unknown size / line size but I'm having some issues detecting the new line character.
When I run the program, it never reaches the end of the line point within the while loop in readFile and will just run constantly. If I run print each character, it prints out some unknown char.
I've tried setting ch to be an int value and typecasting to char for \n comparison. It's not reaching the EOF condition either so I'm not sure what is going on.
code:
void readFile(FILE* file)
{
int endOfFile = 0;
while (endOfFile != 1)
{
endOfFile = readLine(file);
printf("%d\n", endOfFile);
}
}
int readLine(FILE* file)
{
static int maxSize = LINE_SIZE;
int currentIndex = 0;
int endOfFile = 0;
char* buffer = (char*) malloc(sizeof(char) * maxSize);
char ch;
do
{
ch = fgetc(file);
if ((ch != EOF) || (ch != '\n'))
{
buffer[currentIndex] = (char) ch;
currentIndex += 1;
}
if (currentIndex == maxSize)
{
printf("Reallocating string buffer");
maxSize *= 2;
buffer = (char*) realloc(buffer, maxSize);
}
} while ((ch != EOF) || (ch != '\n'));
if (ch == EOF)
{
endOfFile = 1;
}
parseLine(buffer);
free(buffer);
return endOfFile;
}
If someone could help me that would be greatly appreciated because I have been stuck on this issue for quite some time. Thanks in advance.
(ch != EOF) || (ch != '\n')
This is always true.
You want an && (AND) here, both in your if and while, otherwise it will never stop.
Just use this boilerplate standard construct
int ch; /* important, EOF is -1, not in the range 0-255 */
FILE *fp;
/* double brackets prevent warnings about assignment in if */
while ( (ch = fgetc(fp)) != EOF)
{
/* we now have a valid character */
/* usually */
if(ch == endofinputIlike)
break;
}
/* here you have either read all the input up to what you like
or skip because of EOF. usually you will set N or something,
or if N == 0 it was EOF, or we can test ch for EOF */
Generally assignment in if is a bad idea, but this particular snippet is so idiomatic that every experienced C programmer will instantly recognise it.

how to read input string until a blank line in C?

first of all i'm new to coding in C.
I tried to read a string of unknowns size from the user until a blank line is given and then save it to a file, and after that to read the file.
I've only managed to do it until a new line is given and I don't know how to look for a blank line.
#include <stdio.h>
#include <stdlib.h>
char *input(FILE* fp, size_t size) {
char *str;
int ch;
size_t len = 0;
str = realloc(NULL, sizeof(char)*size);
if (!str)return str;
while (EOF != (ch = fgetc(fp)) && ch != '\n') {
str[len++] = ch;
if (len == size) {
str = realloc(str, sizeof(char)*(size += 16));
if (!str)return str;
}
}
str[len++] = '\0';
return realloc(str, sizeof(char)*len);
}
int main(int argc, const char * argv[]) {
char *istr;
printf("input string : ");
istr = input(stdin, 10);
//write to file
FILE *fp;
fp = fopen("1.txt", "w+");
fprintf(fp, istr);
fclose(fp);
//read file
char c;
fp = fopen("1.txt", "r");
while ((c = fgetc(fp)) != EOF) {
printf("%c", c);
}
printf("\n");
fclose(fp);
free(istr);
return 0;
}
Thanks!
I would restructure your code a little. I would change your input() function to be a function (readline()?) that reads a single line. In main() I would loop reading line by line via readline().
If the line is empty (only has a newline -- use strcmp(istr, "\n")), then free the pointer, and exit the loop. Otherwise write the line to the file and free the pointer.
If your concept of an empty line includes " \n" (prefixed spaces), then write a function is_only_spaces() that returns a true value for a string that looks like that.
While you could handle the empty line in input(), there is value in abstracting the line reading from the input termination conditions.
Why not use a flag or a counter. For a counter you could simply increase the counter each character found. If a new line is found and the counter is 0 it must be a blank line. If a new line character is found and the counter is not 0, it must be the end of the line so reset the counter to 0 and continue.
Something like this:
int count = 0;
while ((ch = fgetc(fp)) != EOF)
{
if(ch == '\n')
{
if(count == 0)
{
break;
}
count = 0;
str[len++] = ch;
}
else
{
str[len++] = ch;
ch++;
}
}
Another way would be to simply check if the last character in the string was a new line.
while ((ch = fgetc(fp)) != EOF)
{
if(ch == '\n' && str[len - 1] == '\n')
{
break;
}
}
A blank line is a line which contains only a newline, right ? So you can simply keep the last 2 characters you read. If they are '\n', then you have detected a blank line : the first '\n' is the end of the previous line, the second one is the end of the current line (which is a blank line).
char *input(FILE* fp, size_t size) {
char *str;
int ch, prev_ch;
size_t len = 0;
str = realloc(NULL, sizeof(char)*size);
if (!str)return str;
while (EOF != (ch = fgetc(fp)) && (ch != '\n' && prev_ch != '\n')) {
str[len++] = ch;
if (len == size) {
str = realloc(str, sizeof(char)*(size += 16));
if (!str)return str;
}
prev_ch = ch;
}
str[len++] = '\0';
return realloc(str, sizeof(char)*len);
}
Note that parenthesis around ch != '\n' && prev_ch != '\n' are here to make the condition more understandable.
To improve this, you can keep your function that reads only a line and test if the line returned is empty (it contains only a '\n').

Read all data from stdin C

I've wrote this small function to read all the data from stdin.
I need to know if this function is POSIX compatible (by this, I mean it will work under Unix and Unix-like systems) at least it works on Windows...
char* getLine()
{
int i = 0, c;
char* ptrBuff = NULL;
while ((c = getchar()) != '\n' && c != EOF)
{
if ((ptrBuff = (char*)realloc(ptrBuff, sizeof (char)+i)) != NULL)
ptrBuff[i++] = c;
else
{
free(ptrBuff);
return NULL;
}
}
if (ptrBuff != NULL)
ptrBuff[i] = '\0';
return ptrBuff;
}
The function reads all the data from stdin until get '\n' or EOF and returns a pointer to the new location with all the chars. I don't know if this is the most optimal or safer way to do that, and neither know if this works under Unix and Unix-like systems... so, I need a little bit of help here. How can I improve that function? or is there a better way to get all the data from stdin without leaving garbage on the buffer? I know that fgets() is an option but, it may leave garbage if the user input is bigger than expected... plus, I want to get all the chars that the user has written.
EDIT:
New version of getLine():
char* readLine()
{
int i = 0, c;
size_t p4kB = 4096;
void *nPtr = NULL;
char *ptrBuff = (char*)malloc(p4kB);
while ((c = getchar()) != '\n' && c != EOF)
{
if (i == p4kB)
{
p4kB += 4096;
if ((nPtr = realloc(ptrBuff, p4kB)) != NULL)
ptrBuff = (char*)nPtr;
else
{
free(ptrBuff);
return NULL;
}
}
ptrBuff[i++] = c;
}
if (ptrBuff != NULL)
{
ptrBuff[i] = '\0';
ptrBuff = realloc(ptrBuff, strlen(ptrBuff) + 1);
}
return ptrBuff;
}
LAST EDIT:
This is the final version of the char* readLine() function. Now I can't see more bugs neither best ways to improve it, if somebody knows a better way, just tell me, please.
char* readLine()
{
int c;
size_t p4kB = 4096, i = 0;
void *newPtr = NULL;
char *ptrString = malloc(p4kB * sizeof (char));
while (ptrString != NULL && (c = getchar()) != '\n' && c != EOF)
{
if (i == p4kB * sizeof (char))
{
p4kB += 4096;
if ((newPtr = realloc(ptrString, p4kB * sizeof (char))) != NULL)
ptrString = (char*) newPtr;
else
{
free(ptrString);
return NULL;
}
}
ptrString[i++] = c;
}
if (ptrString != NULL)
{
ptrString[i] = '\0';
ptrString = realloc(ptrString, strlen(ptrString) + 1);
}
else return NULL;
return ptrString;
}
POSIX-compatible: yes!
You're calling only getchar(), malloc(), realloc() and free(), all of which are
standard C functions and therefore also available under POSIX. As far as I can tell, you've done all the necessary return code checks too. Given that, the code will be good in any environment that supports malloc() and stdin.
Only thing I would change is the last call to strlen, which is not necessary since the length is already stored in i.

Resources