I'm writing a BF interpreter in C and I've run into a problem reading files. I used to use scanf in order to read the first string, but then you couldn't have spaces or comments in your BF code.
Right now here is what I have.
char *readFile(char *fileName)
{
FILE *file;
char *code = malloc(1000 * sizeof(char));
file = fopen(fileName, "r");
do
{
*code++ = (char)fgetc(file);
} while(*code != EOF);
return code;
}
I know the problem arises in how I'm assigning the next char in the file to the code pointer but I'm just not sure what that is.
My pointer knowledge is lacking which is the point of this exercise.
The interpreter works fine, all using pointers, I'm just having a problem reading files in to it.
(I'm going to implement only reading +-><[]., into the file later, although if anyone has a good way to do it, it would be great if you'd let me know!)
There are a number of things wrong with your code:
char *readFile(char *fileName)
{
FILE *file;
char *code = malloc(1000 * sizeof(char));
file = fopen(fileName, "r");
do
{
*code++ = (char)fgetc(file);
} while(*code != EOF);
return code;
}
What if the file is greater than 1,000 bytes?
You are increasing code each time you read a character, and you return code back to the caller (even though it is no longer pointing at the first byte of the memory block as it was returned by malloc).
You are casting the result of fgetc(file) to char. You need to check for EOF before casting the result to char.
It is important to maintain the original pointer returned by malloc so that you can free it later. If we disregard the file size, we can achieve this still with the following:
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL)
return NULL; //could not open file
code = malloc(1000);
while ((c = fgetc(file)) != EOF)
{
code[n++] = (char) c;
}
// don't forget to terminate with the null character
code[n] = '\0';
return code;
}
There are various system calls that will give you the size of a file; a common one is stat.
Expanding upon the above code from #dreamlax
char *readFile(char *fileName) {
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL) return NULL; //could not open file
fseek(file, 0, SEEK_END);
long f_size = ftell(file);
fseek(file, 0, SEEK_SET);
code = malloc(f_size);
while ((c = fgetc(file)) != EOF) {
code[n++] = (char)c;
}
code[n] = '\0';
return code;
}
This gives you the length of the file, then proceeds to read it character by character.
Here's one simple way to ignore everything but valid brainfuck characters:
#define BF_VALID "+-><[].,"
if (strchr(BF_VALID, c))
code[n++] = c;
the file is being opened and not closed for each call to the function also
I think the most significant problem is that you're incrementing code as you read stuff in, and then returning the final value of code, i.e. you'll be returning a pointer to the end of the string. You probably want to make a copy of code before the loop, and return that instead.
Also, C strings need to be null-terminated. You need to make sure that you place a '\0' directly after the final character that you read in.
Note: You could just use fgets() to get the entire line in one hit.
Either of the two should do the trick -
char *readFile(char *fileName)
{
FILE *file;
char *code = malloc(1000 * sizeof(char));
char *p = code;
file = fopen(fileName, "r");
do
{
*p++ = (char)fgetc(file);
} while(*p != EOF);
*p = '\0';
return code;
}
char *readFile(char *fileName)
{
FILE *file;
int i = 0;
char *code = malloc(1000 * sizeof(char));
file = fopen(fileName, "r");
do
{
code[i++] = (char)fgetc(file);
} while(code[i-1] != EOF);
code[i] = '\0'
return code;
}
Like the other posters have pointed out, you need to ensure that the file size does not exceed 1000 characters. Also, remember to free the memory when you're done using it.
The problem here is twofold
a) you increment the pointer before you check the value read in, and
b) you ignore the fact that fgetc() returns an int instead of a char.
The first is easily fixed:
char *orig = code; // the beginning of the array
// ...
do {
*code = fgetc(file);
} while(*code++ != EOF);
*code = '\0'; // nul-terminate the string
return orig; // don't return a pointer to the end
The second problem is more subtle -fgetc returns an int so that the EOF value can be distinguished from any possible char value. Fixing this uses a temporary int for the EOF check and probably a regular while loop instead of do / while.
Related
I have a text file, and I open it and read one line of it, and close the text file. I'm calling my function under a for loop, but each time this function reads the first line of a text file, how can I fix it to read from the continuation
You can use fseek to reposition yourself in the file after closing and reopening, but it is very unusual to do so. So unusual, in fact, that I would suggest it is completely wrong. Here's some sample code that demonstrates how to do that, as well as a more typical loop. Each loop here reads the first 2 lines of the file, assuming each line is sufficiently small; handling long lines is beyond the scope of this question.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
FILE * xfopen(const char *path, const char *mode);
void xfseek(FILE *stream, long offset, int whence, const char *);
long xftell(FILE *stream, const char *);
void xfclose(FILE *stream, const char *);
int
main(int argc, char *argv[])
{
const char *path = argc > 1 ? argv[1] : "input";
/* Read the first two lines of the file, closing the file on each
* iteration. This is ** not ** the usual way to do this, and is
* included here for demonstration
* purposes only. DO NOT DO THIS.
* It is very unusual to close and re-open the file on each iteration.
*/
long position = 0;
for( int line = 1; line < 3; line++ ){
FILE *ifp = xfopen(path, "r");
char buf[1024];
xfseek(ifp, position, SEEK_SET, path);
fgets(buf, sizeof buf, ifp); /* (1) */
printf("line %d: %s", line, buf);
position = xftell(ifp, path);
xfclose(ifp, path); /* !! */
}
/* The more usual way to read each line of a file is to simply
* read it with repeated calls to the appropriate read method
* (fgets, fread, fgetc, etc.) Each subsequent read starts
* where the previous read finished.
*/
FILE *ifp = xfopen(path, "r");
for( int line = 1; line < 3; line++ ){
char buf[1024];
fgets(buf, sizeof buf, ifp); /* (1) */
printf("line %d: %s", line, buf);
}
xfclose(ifp, path);
return 0;
}
FILE *
xfopen(const char *path, const char *mode)
{
FILE *fp = path[0] != '-' || path[1] != '\0' ? fopen(path, mode) :
*mode == 'r' ? stdin : stdout;
if( fp == NULL ){
perror(path);
exit(EXIT_FAILURE);
}
return fp;
}
void
xfseek(FILE *stream, long offset, int whence, const char *name)
{
if( fseek(stream, offset, whence) == -1){
perror(name);
exit(EXIT_FAILURE);
}
}
long
xftell(FILE *stream, const char *name)
{
long ret = ftell(stream);
if( ret == -1 ){
perror(name);
exit(EXIT_FAILURE);
}
return ret;
}
void
xfclose(FILE *stream, const char *name)
{
if( fclose(stream) ){
perror(name);
exit(EXIT_FAILURE);
}
}
Notes: (1) It is left as an exercise for the reader how best to handle short reads (eg, when fgets returns NULL) or long lines (eg, when fgets completely fills the buffer but fails to read an entire line). Perhaps it is a bit of a cop-out to leave that as an exercise, but the annoyance of dealing with those issues points strongly towards reasons for using the standard idiom. If you want to print the first two lines of the file, use some variation of for( int count = 0; (c = fgetc(fp)) != NULL && count < 2; ) { if( c == '\n' ) count += 1; putchar(c); }. Putting the read function as a condition of the loop is (almost) always the best choice.
The comments have already made suggestions on other alternatives for what you are attempting. But regardless whether it is the right approach or not, it seems pretty clear that your stated ask is clear about wanting to use fseek() et. al to view successive lines when opening and closing a file.
To open and close a file, and each time access and display a successive line, you must first know where each of the locations to be viewed are located within that file. Indeed, as you have tagged, fseek(), (as well as ftell()) can be used to do this. The following pseudo code steps illustrate one possibility:
//store file pointer locations of each line in file:
FILE *fp = fopen(fn, "r");
if(fp)
{
for(int i = 0; i < l_cnt; i++)
{
pos[i] = ftell(fp);
fgets(line, sizeof line, fp);
}
}
fclose(fp);
Then...
//alternately open and close file to view successive lines at stored positions
for(int i = 0; i < line_cnt; i++)
{
FILE *fp = fopen(fn, "r");
if(fp)
{
fseek(fp, pos[i], 0);
fgets(line, sizeof line, fp);
printf("line %d: %s\n", i, line);
fclose(fp);
}
}
There is a more complete source and run-time example here
I am creating a program that needs to read in an arbitrary amount of lines of arbitrary length each (but for now assuming one string per line). So basically, I need a dynamic array of dynamic string. I figured the easiest way to do this would be with a char double pointer. But I am getting errors with the asprintf statement. particularly "strings[track]". I figured this would point to the first char array. Any help on how to do this?
Here is the code so far:
char **strings;
int readFile(char* filename)
{
FILE *fp;
fp = fopen(filename, "r");
if (fp == NULL)
{
return -1;
}
char *singleLine = NULL;
ssize_t lineSize;
size_t amount = 0;
int track = -1;
while((lineSize = getline(&singleLine, &amount, fp)) != -1)
{
track++;
asprintf(&strings[track],"%s",singleline));
}
}
i have a function that reads content of a file more exactly the first line, measures the length of what it finds and returns it. Initially the function printed what it read but i modify it to "return buffer"
here is my function:
char *get_process() {
int len = 0;
FILE *fp;
char c;
char *buffer = NULL;
fp = fopen("input.txt", "r");
if (fp == NULL) {
return 1;
}
while((c = fgetc(fp)) != EOF) {
if (c == '\n') {
break;
}
len++;
buffer = (char *) malloc(sizeof(char) * len);
fseek(fp, 0, SEEK_SET);
fread(buffer, sizeof(char), len, fp);
fclose(fp);
return buffer;
}
the content of input.txt is something like /etc/config.conf
now my problem is that i want to do is assign what this function returns to a variable that i use in main() function.
what i did i have added to my main function:
char *PID = get_process();
But i have the following error on compilation:
:760: warning: return makes pointer from integer without a cast
I'm doing something wrong of course like always.
But what is it?
If your fp==NULL then your function will return 1;. Thats why you are getting this warning. Instead you can return NULL; or return nullptr; to indicate that something went wrong.
[EDIT]
If your code added in the question is exactly what you have written in your program, then you have one more problem, as your while loop closing brace is not placed
I have the following code that converts a stream data of 16-bit integer to unsigned 8-bit integer.
I am looking to convert them to alphabetical data values and see what they contain.
#include<stdio.h>
int main() {
FILE *fp,*out;
char buffer[256];
size_t i = 0;
fp=fopen("c:/Gosam/input.txt", "rb");
if(fp != NULL) {
fread(buffer, sizeof buffer,1, fp);
}
out = fopen("c:/Gosam/res.txt", "w");
if(out != NULL) {
// buffer = (char*) malloc (sizeof(char)*Size);
for( i = 0; i < sizeof(buffer); i += 2)
{
const unsigned int var = buffer[i] + 256 * buffer[i + 1];
fprintf(out, "%u\n", var);
}
fclose(out);
}
fclose(fp);
}
The following is the form of my output:
263 4294966987 4294967222 4294967032 64 4294967013 73 4294967004 90
4294967028 83 4294966975 37 4294966961 5 4294966976 82 4294966942
4294967022 4294966994 11 4294967024 29 4294966985 4294966986 4294966954 50
4294966993 4294966974 4294967019 4294967007
This are the values I want to convert to alphabetical characters and see their content.
I don't know what you expect as an answer (you didn't ask a question), but there seems to be one suspicious thing in your code:
char buffer[256];
Here char means signed char. If your code does manipulations on them (like multiplying by 256), it probably doesn't do what you expect (though I can only guess what you expect - your question doesn't mention it).
Try the following:
unsigned char buffer[256];
Also please ask a question (that is, something with a question mark), and give some examples (input, output).
Your basic mistakes were:
after opening the inputfile checking out instead of fp against NULL
fread until eof won't return the number of characters that could be read (I've used fseek and ftell for this purpose)
writing uint values instead of char values to your file
I've fixed them and commented the affected lines appropriate. I also changed the buffer to use dynamic memory allocation instead of static allocation (that's how you can allocate memory for a buffer of a size that is unknown at compile-time). Please try the following code, which will copy all ASCII characters from one file to your output file (which is probably what you meant by 'alphabetical strings'):
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]){
FILE *fp, *out;
char *buffer = NULL; /* use a pointer for dynamic memory allocation */
size_t i = 0, charCount = 0;
fp = fopen("c:/input.txt", "r"); /*read as ascii - not binary */
if(fp != NULL){ /*use 'fp' here 'out' is not initalized */
fseek(fp, 0, SEEK_END); /* go to end of the file */
charCount = ftell(fp) - 1; /* get position */
fseek(fp, 0, SEEK_SET); /* return to the beginning of the file */
buffer = (char*)malloc(sizeof(char)*charCount); /* allocate memory */
fread(buffer, sizeof(char) * charCount, 1, fp); /* reads all characters from the file */
}
out = fopen("c:/output.txt", "w");
if(out != NULL){
for(i = 0; i < charCount; i += 1){ /* loop from 0 to count of characters */
const unsigned char var = buffer[i];
fprintf(out, "%c", var);
}
fclose(out);
}
fclose(fp);
if(buffer != NULL){
free(buffer); /* deallocate memory */
}
return 0;
}
For some reason when I try to write through a pipe with fputc my program doesn't work; however, it works fine when I use the write system call. Here is the part of my code using fputc:
FILE *input = fopen(argv[1], "rb");
FILE *toSort = fdopen(ps_fd[1], "wb");
/* close the side of pipe I am not going to use */
close (ps_fd[0]);
char temp;
char buf[1];
while ((temp=fgetc(input)) != EOF)
{
buf[0] = (char)temp;
fputs(buf, toSort);
buf[0] = '\0';
}
fputs(buf, toSort);
close(ps_fd[1]);
use fflush(toSort) after fputs()
The question title asks about fputc() but the code (mis)uses fputs().
Note that fputs() expects a null-terminated string. It is not appropriate for binary data; it won't write a zero (or null) byte.
Additionally, you are not null terminating the string. You are not providing enough storage for the null termination. You are not closing the file correctly. You should use int temp since fgetc() returns an int, not a char. The minimum changes needed to use fputs() are:
FILE *input = fopen(argv[1], "rb");
FILE *toSort = fdopen(ps_fd[1], "wb");
close(ps_fd[0]);
int temp;
char buf[2] = ""; // Two characters allocated; null terminated
while ((temp = fgetc(input)) != EOF)
{
buf[0] = (char)temp;
fputs(buf, toSort);
}
fclose(toSort); // fclose() to flush the buffered data
Or, using fputc():
FILE *input = fopen(argv[1], "rb");
FILE *toSort = fdopen(ps_fd[1], "wb");
close(ps_fd[0]);
int temp;
while ((temp = fgetc(input)) != EOF)
fputc(temp, toSort);
fclose(toSort);