How to split a string along a line in C? [duplicate] - c

This question already has answers here:
Split string by a substring
(5 answers)
Split string with delimiters in C
(25 answers)
Closed last month.
I've been following the Cherno's opengl series in c++ recently and wanted to give it a try in c. One thing I have been having a problem with is reading in the contents of the shader files so I can compile them. While I can read in the whole file, in the videos, it is recommended to use one file for both the vertex and fragment shaders. I need a way to break the file into two separate strings, one for the vertex shader, and one for the fragment shader. To help with this, at the top of each shader I added "#shader vertex" and "#shader fragment" to know when I need to split, as was recommended in the video. However, I don't really know how to go about this in c as the video was in c++ using fstream rather than c's file api.
It would be best to be able to read the string or file line by line to check if the markers are found and then write the contents / copy from the buffer to either the vertex or fragment shader strings as needed.
So far I have this
static char* FileToString(const char* filePath)
{
//Load .shader file into two separate strings (one for vertex and one for fragment)
FILE* source = fopen(filePath, "r");
if(source == NULL)
{
printf("Failed to open %s!\n", filePath);
}
char* buffer = 0;
long length;
fseek(source, 0, SEEK_END);
length = ftell(source);
fseek(source, 0, SEEK_SET);
buffer = (char*)malloc(length);
if(buffer)
{
fread(buffer, 1, length, source);
}
fclose(source);
return buffer;
}
Thanks for any help!

assuming vertex is first, do this after you read the whole file:
char *vertex = buffer + sizeof("#shader vertex") - 1;
char *fragment = strstr(vertex, "#shader fragment");
*fragment = '\0';
fragment += sizeof("#shader fragment") - 1;
this is the easiest way, after that you get 2 pointers, each pointing to a string containing your data. Assuming the data can be read as text. If it's binary data that can contain 0's, your approach of adding text will not easily work

Related

Storing strings in array in C

I have read a lot of questions on this, and using them I have altered my code and have created code which I thought would work.
I think it's my understanding of C, which is failing me here as I can't see where I'm going wrong.
I get no compilation errors, but when I run i receive 'FileReader.exe has stopped working' from the command prompt.
My code is :
void storeFile(){
int i = 0;
char allWords [45440][25];
FILE *fp = fopen("fileToOpen.txt", "r");
while (i <= 45440){
char buffer[25];
fgets(buffer, 25, fp);
printf("The word read into buffer is : %s",buffer);
strcpy(allWords[i], buffer);
printf("The word in allWords[%d] is : %s", i, allWords[i]);
//allWords[i][strlen(allWords[i])-1] = '\0';
i = i + 1;
}
fclose(fp);
}
There are 45440 lines in the file, and no words longer than 25 char's in length. I'm trying to read each word into a char array named buffer, then store that buffer in an array of char arrays named allWords.
I am trying to get this part working, before I refactor to return the array to the main method (which I feel won't be a fun experience).
You are trying to allocate more than a megabyte (45440*25) worth of data in automatic storage. On many architectures this results in stack overflow before your file-reading code even gets to run.
You can work around this problem by allocating allWords statically, like this
static char allWords [45440][25];
or dynamically, like this:
char (*allWords)[25] = malloc(45440 * sizeof(*allWords));
Note that using buffer in the call to fgets is not required, because allWords[i] can be used instead, without strcpy:
fgets(allWords[i], sizeof(*allWords)-1, fp);
Also note that an assumption about file size is unnecessary: you can continue calling fgets until it returns NULL; this indicates that the end of the file has been reached, so you can exit the loop using break.

Conway's Game of Life FileIO

I'm working on learning C and decided to port my Game of Life code over from Java. It doesn't seem too difficult except that the FileIO part. My input file looks something like:
Beehive
5 6
------
--XX--
-X--X-
--XX--
------
Here's the pseduo-code of what I did in Java;
Scanner to open the file,
String line = file.nextLine(),
print the line,
get the second line
Trim and split the firstLine,
String[tokens[0]][tokens[1]],
while(line != NULL) -> string[row][col] = input.charAt(i);
close input,
return string[][]
This is what I have so far in C,
void fileIO() {
FILE *file;
char buffer[BUFFER_SIZE];
file = fopen("INPUT_FILE", "r");
if(file == NULL) {
printf("Cannot open file!");
}
while(fgets(buffer, BUFFER_SIZE, file) != NULL ) {
}
}
I'm not sure how to proceed from here? Can anyone give me a pointer in which way to go from here?
To print a line: puts
puts(buffer);
Note that after fgets, buffer contains the newline character \n, which will be printed too. I guess you want this behavior (not sure how this works in Java).
To trim and split a line: sscanf
int height, width;
...
sscanf(buffer, "%d%d", &height, &width);
To extract a character from a string (instead of input.charAt(i)):
char c = buffer[i];
This is not file I/O; it's just the C syntax for getting a character from a string.
It seems that you have a function in Java that returns a 2-D array (of characters? of strings?), which is dynamically allocated. Java supports recording the width and height in the array object itself, while C doesn't support this. Instead of the 2-D array, you will have to use a struct:
struct MyDataFromFile
{
int height, width;
bool **data;
};
Such data structure is only one possible option; you could use different options:
bool[MAX_HEIGHT][MAX_WIDTH] - convenient if there is maximum height and width
uint64_t *data if you want to use 1 bit for storage, instead of 1 byte - this requires additional bit-fiddling

Reading a File into an array of Structures in C

still really new to C but starting to get the hang of it....
My program is supposed to create/write a file and store information from an array of structures. That part is fine. What im having trouble with is reading from that file back into an empty array of structures....
here's my structs:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX 100
struct Video {
char name[1024]; //name
int ranking; // Number of viewer hits
char url[1024]; // YouTube URL
};
struct Video Collection[MAX];
here's my load method which reads from my file back into my array of structures:
void load()
{
FILE *fileName;
fileName = fopen("ranking.dbm", "rb");
if (fileName != NULL){
fread (Collection,1,1,fileName);
}
else {
printf("ERROR");
}
}
also here is my write method:
void save()
{
FILE * pFile;
pFile = fopen ( "Ranking.dbm" , "wb" );
fwrite (Collection, 1 , sizeof(Collection), pFile );
fclose (pFile);
}
however when i print out my array collection after loading.... its empty... even though i can see my file in the project folder and open it and verify that the data is in there....
am i correct in thinking that i dont need a buffer since i don't need to do any processing on it before using it?
also since i've already statically allocated space for memory.... am i correct in thinking that i can just read directly into the array?
here is my print code:
void printall()
{
int i;
printf("\nCollections: \n");
for(i = 0; i < tail; i++)
{
printf("\nVideo Name: %s", Collection[i].name);
printf("\nRanking (Hits): %d", Collection[i].ranking);
printf("\nURL: %s", Collection[i].url);
printf("\n");
}
}
fread is in fact designed to read arrays of structures from a file, but you have to use it correctly.
fread's four parameters are as follows:
void * ptr, size_t size, size_t count, FILE * stream
The first parameter is where to put the data (in your case, Collection). The second parameter is the size of each array element: in your case, you want to put sizeof(struct Video). The third parameter is the number of elements you want to read, in your case, MAX. The fourth parameter is the file to read from.
If you want to read into an array like struct Video Collection[MAX], you would then use fread(Collection, sizeof(struct Video), MAX, file). fread will return the total number of elements read, which will be ≤ MAX.
I'm seeing a few issues here.. first how you read the file:
fread (Collection,1,1,fileName);
This will read into collection 1 byte from fileName into Collection
You should check the return status of fread(), when it's successful it tells you the total number of bytes to be read. (parameter 2 * parameter 3, or 1*1 in your case).
When I modify your read code like this:
fread(Collection, sizeof(struct Video), 1, fileName);
It does successfully read from the file... however you have a different problem now. Let's say your file contained this:
something 5 http://something.com
nothing 3 http://nothing.com
So (I think) that's the format for your file, a name (ASCII), a ranking (int), and URL (ASCII). Now let's say your main() function looked like this:
int main ()
{
load();
printall();
return 0;
}
What you'd get back on stdout would be something like:
Collections:
Video Name: something 6 http://something.com
nothing 3 http://nothing.com
Ranking (Hits): 0
URL:
The reason is because you declared your array with static (and very large) elements. The fread() will try to read in the sizeof(struct Video) which is 1024+4+1024 bytes, so unless every one of your lines is the exact size (1024 chars for name and url) then you're going to get what looks like messed up or empty data.
I would suggest reading until you hit a space instead and storing each value in the correct element instead of trying to read out the full file into an array.
EDIT:
If you want to populate your array like:
fread(myarray, sizeofstruct, numberofstructs, file);
You have to guarantee the data length. In your example you'd have to say "name is however many characters, + blank spaces = 1024" and same for URL. That seems to be a horrible space waster. The better bet is to populate your array one element at a time:
for(0 to last_element){
set myarray.name = data until first space
set myarray.ranking = (int)data until second space
set myarray.url = data until newline
}
You can use fscanf() to read until a whitespace.
Frankly if you're going to populate one element at a time I'd just use character pointers for name and url and dynamically assign memory so you don't have huge wasted arrays.
First I have to assume you meant struct Video Collection[MAX];else your upper part is invalid C.
Second: you are reading 1 byte into Collection.
Try
fread(Collection, sizeof(struct Video), MAX, fileName);
This will read up to MAX times chunks of sizeof(struct Video)bytes into Collection.

Load file into 2D char Array in C

I have a text file with 25 lines, each with 34 characters on.
In C how is it possible to load these characters and store them into a 2D array?
If the first three lines of the file are such:
bamaaaaaaaacxxxxxxxxxxbaaaaaaaamac
jzjzzzzzzzzdaaaaaaaaaaezzzzzzzzjzj
jzjzbaaczgzzzzzzzzzzzzzzgzbaaczjzj
...and so on
I require the array to be stored as if it was defined like this:
char* data[] = {
"baaaaaaaaaaaaaacxxbaaaaaaaaaaaaaac",
"jzzzzzzzzzzzzzzjxxjzzzzzzzzzzzzzzj",
"jzbaaaaaaaaaaaaexxdaaaaaaaaaaaaczj",
...and so on
Hopefully this makes some sense! It is important that the type of data is char data[][] as it is used in that format in the rest of my project and cannot be changed.
I have done the basic begining of the File IO
FILE *infp;
printf("Opening file\n");
if((infp = fopen("file.txt", "r"))== NULL) {
printf("\nERROR : Unable to open input file\n");
SetExitWithCode( 999 );
}else{
//code here
}
Can anyone help?
So, you want your array to look like this:
char data[25][35] //There is an extra 1 character per line to hold the null terminator character
Then simply read the whole file into that array
fread(data, 1, sizeof(data), infp);
And finally, replace the new line character in on each line with a terminator
for (int i = 0; i < 25; ++i) {
data[i][34] = 0;
}
This is the easiest solution to the problem, but it is also a bad way of doing it. It does not validate that the data is in the correct format, and everything is hard coded.

string replace in file using C

I haven't yet implemented this, I'm still in the thinking stage, but I have to go through a file and replace a certain string with another string. For example,
<img src="/images/logo.gif" ...
should become
<img src="/proxy/www.example.com/images/logo.gif" ...
Any advice on how I can approach this? Perhaps there exist some "string replace" C functions that would do this for me that I don't know about...?
Right now, if I had to write this function myself, I would give it as parameters the file, string to replace, replacement string. Then I would manually go through the file and look for an occurrence of the string and recreate it. This, however, seems very inefficient. Are there better ways to do this?
Thanks,
Hristo
No, there is no function in C that replaces a string throughout a file. You must implement it yourself.
That said, what you're showing us is HTML, and HTML is tricky, because it's hierarchical. Are you required to correctly parse it? Because if you are, the task is much more difficult. Seeing that it's homework, I doubt it, so you might do enough by:
open the file and load it to memory (assuming it isn't too large - if it is, you can write into a temporary file and move it onto the original one after you've finished)
continuously use strstr to find the anchor string you need to start replacing
replace
repeat 2 and 3 until finished with file
write file back
Since it's homework I'm going with the assumption that the string can not span multiple lines. If this assumption is correct (and barring the complications with "replacing text in HTML") then:
1 Read the next line
2 Replace string and write line (to another file)
3 If not at end, goto #1
4 Win \o/
Or perhaps the teacher wants something else shrug
First of all, C is an awesome language, but is one of the most painful languages to do this type of operation in. Just had to say it.
Can you safely assume that the contents of the entire file can fit in memory? If so:
allocate buffer big enough to hold file contents
read entire file into buffer
inputPtr = 0
while(inputPtr < size of buffer) {
replacePosition = strstr(inputPtr, stringToReplace);
if (replacePosition != NULL)
writeUntil = replacePosition - 1
else
writeUntil = end of buffer
write out buffer from inputPtr to writeUntil inclusive (could be 0 bytes)
if (replacePosition == NULL) break
write out the replacement string
inputPtr = replacePosition + strlen(stringToReplace)
}
Since this is homework, I won't give you an answer but I'll point out a classic issue that trips people up.
In C, it's easiest to read a fixed byte count (you can try to do line by line but if a line is too long, that reverts to reading a fixed number of bytes). If the string you are trying to replace ends up getting split between one buffer and a second buffer:
buf1 -> "...<img src=\"/ima"
buf2 -> "ges/logo.gif\"..."
you won't be able to do a simple search replace in memory.
Are you try strcpy function for this,
Assign the url in one string and replace it by strcpy function.
You should investigate the sed command. See what it does and do something similar.
It works as a filter, so when using it to replace something in a file what you often do is capture the output into a file and then replace the old file with the new file.
You can use following program to search & replace string in a file.
int main()
{
.....
replaceIPAddress( "System.cfg", "172.16.116.157", "127.0.0.1");
.....
}
void replaceIPAddress( char * confFileName, char *text_to_find , char *text_to_replace )
{
FILE *input = fopen(confFileName, "r");
FILE *output = fopen("temp.txt", "w");
char buffer[512];
while (fgets(buffer, sizeof(buffer), input) != NULL)
{
char *pos = strstr(buffer, text_to_find);
if (pos != NULL)
{
/* Allocate memory for temporary buffer */
char *temp = calloc(
strlen(buffer) - strlen(text_to_find) + strlen(text_to_replace) + 1, 1);
/* Copy the text before the text to replace */
memcpy(temp, buffer, pos - buffer);
/* Copy in the replacement text */
memcpy(temp + (pos - buffer), text_to_replace, strlen(text_to_replace));
/* Copy the remaining text from after the replace text */
memcpy(temp + (pos - buffer) + strlen(text_to_replace),
pos + strlen(text_to_find),
1 + strlen(buffer) - ((pos - buffer) + strlen(text_to_find)));
fputs(temp, output);
free(temp);
}
else
fputs(buffer, output);
}
fclose(output);
fclose(input);
/* Rename the temporary file to the original file */
rename("temp.txt", confFileName);
}

Resources