i'm studying C, and I need to read a text-file, but I can only use "write, malloc, free, open, read, close".
That's my code:
#define MAXCHAR 10000
int open_fp(int check)
{
char *int_vector;
int fp,len;
int i,j;
char buffer[MAXCHAR];
if(check == 0) //standard list
{
if((fp = open("file.txt", O_RDONLY)) != -1) //check if the fp is opened. -1 = error
{
printf("\n%d\n",fp); // DEBUG FUNCTION
sleep(1);
if (!(int_vector = (char*)malloc(sizeof(char) * sizeof(char))))
{
printf("\nWrong allocation\n!"); // DEBUG FUNCTION
return(0);
}
len = read(fp,int_vector,MAXCHAR);
for(i=0;i<len;i++)
{
printf("%c",int_vector[i]);
}
}
else
{
printf("File error!");
return (0);
}
}
return(0);
}
Now my question is: As you can read here,
char buffer[MAXCHAR];
i've created static buffer, but i would like create a dynamic buffer which allow me to resize the buffer according to the number of the chars in the text file, but i don't know how... someone have a trick😥😥 ?
First of all your way of allocating memory is wrong in below line.
//This allocates only 2 bytes of memory, but you are trying to read 10000
if (!(int_vector = (char*)malloc(sizeof(char) * sizeof(char))))
correct that line as below
//better to add one byte extra and store \0 at the end, useful in case of string operations
if (!(int_vector = malloc(MAXCHAR+1)))
and as far as your question is concerned, you dont need to reallocate memory in this particular case because you are just reading the bytes to buffer and printing.
a single malloc will suffice.
#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAXCHAR 100
int open_fp(int check)
{
char *int_vector;
int fp,len;
int i,j;
char buffer[MAXCHAR];
if(check == 0) //standard list
{
if((fp = open("file.txt", O_RDONLY)) != -1) //check if the fp is opened. -1 = error
{
printf("\n%d\n",fp); // DEBUG FUNCTION
sleep(1);
if (!(int_vector = (char*)malloc(MAXCHAR)))
{
printf("\nWrong allocation\n!"); // DEBUG FUNCTION
return(0);
}
//not doing memset on purpose because only limited bytes are accessed.
while(len = read(fp,int_vector,MAXCHAR))
{
printf("\n **number of bytes read is %d **\n",len);
for(i=0;i<len;i++)
{
printf("%c",int_vector[i]);
}
}
printf(" At last LEN = %d\n", len);
//free the memory at the end
free(int_vector);
int_vector = NULL;
close(fp);// better to as fd
}
else
{
printf("File error!\n");
return (0);
}
}
return(0);
}
int main()
{
open_fp(0);
return 0;
}
ehm, If you forget to set realloc to it as well, here is some sample code for reallocation (dynamic resizing buffer)
#include <stdio.h>
#include <stdlib.h>
int main () {
char *str;
/* Initial memory allocation */
str = (char *) malloc(15);
strcpy(str, "tutorialspoint");
printf("String = %s, Address = %u\n", str, str);
/* Reallocating memory */
str = (char *) realloc(str, 25);
strcat(str, ".com");
printf("String = %s, Address = %u\n", str, str);
free(str);
return(0);
}
Related
So I have this bit of code
int main(int argc, char *argv[]) {
char *vendas[1];
int size = 1;
int current = 0;
char buffer[50];
char *token;
FILE *fp = fopen("Vendas_1M.txt", "r");
while(fgets(buffer, 50, fp)) {
token = strtok(buffer, "\n");
if (size == current) {
*vendas = realloc(*vendas, sizeof(vendas[0]) * size * 2);
size *= 2;
}
vendas[current] = strdup(token);
printf("%d - %d - %s\n", current, size, vendas[current]);
current++;
}
}
Here's the thing... Using GDB it's giving a segmentation fault on
vendas[current] = strdup(token);
but the weirdest thing is it works up until the size it at 1024. The size grows up to 1024 and then it just spits a segmentation fault at around the 1200 element.
I know the problem is on the memory reallocation, because it worked when I had a static array. Just can't figure out what.
You cannot reallocate a local array, you want vendas to be a pointer to an allocated array of pointers: char **vendas = NULL;.
You should also include the proper header files and check for fopen() and realloc() failure.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
void free_array(char **array, size_t count) {
while (count > 0) {
free(array[--count]);
}
free(array);
}
int main(int argc, char *argv[]) {
char buffer[50];
char **vendas = NULL;
size_t size = 0;
size_t current = 0;
char *token;
FILE *fp;
fp = fopen("Vendas_1M.txt", "r");
if (fp == NULL) {
printf("cannot open file Vendas_1M.txt\n");
return 1;
}
while (fgets(buffer, sizeof buffer, fp)) {
token = strtok(buffer, "\n");
if (current >= size) {
char **savep = vendas;
size = (size == 0) ? 4 : size * 2;
vendas = realloc(vendas, sizeof(*vendas) * size);
if (vendas == NULL) {
printf("allocation failure\n");
free_array(savep, current);
return 1;
}
}
vendas[current] = strdup(token);
if (vendas[current] == NULL) {
printf("allocation failure\n");
free_array(vendas, current);
return 1;
}
printf("%d - %d - %s\n", current, size, vendas[current]);
current++;
}
/* ... */
/* free allocated memory (for cleanliness) */
free_array(vendas, current);
return 0;
}
You only have room for one (1) pointer in you array of char *vendas[1]. So second time around you are outside the limits of the array and are in undefined behavior land.
Also, the first call to realloc passes in a pointer that was not allocated by malloc so there is another undefined behavior.
I am doing a project where I have to read in text from a file and then extract every word that is 4 characters long and allocate it into dynamic array.My approach is to create int function that will get number of 4 letter words and return that number , then create another function that will grab that number and create dynamic array consisting of that many elements. The problem with this approach is how to populate that array with words that meet the requirement.
int func1(FILE *pFile){
int counter = 0;
int words = 0;
char inputWords[length];
while(fscanf(pFile,"%s",inputWords) != EOF){
if(strlen(inputWords)==4){
#counting 4 letter words
counter++;
}
}
}
return counter;
}
int main(){
#creating pointer to a textFile
FILE *pFile = fopen("smallDictionary.txt","r");
int line = 0;
#sending pointer into a function
func1(pFile);
fclose(pFile);
return 0;
}
I would suggest reading lines of input with fgets(), and breaking each line into tokens with strtok(). As each token is found, the length can be checked, and if the token is four characters long it can be saved to an array using strdup().
In the code below, storage is allocated for pointers to char which will store the addresses of four-letter words. num_words holds the number of four-letter words found, and max_words holds the maximum number of words that can currently be stored. When a new word needs to be added, num_words is incremented, and if there is not enough storage, more space is allocated. Then strdup() is used to duplicate the token, and the address is assigned to the next pointer in words.
Note that strdup() is not in the C Standard Library, but that it is POSIX. The feature test macro in the first line of the program may be needed to enable this function. Also note that strdup() allocates memory for the duplicated string which must be freed by the caller.
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SZ 1000
#define ALLOC_INC 100
int main(void)
{
FILE *fp = fopen("filename.txt", "r");
if (fp == NULL) {
perror("Unable to open file");
exit(EXIT_FAILURE);
}
char buffer[BUF_SZ];
char **words = NULL;
size_t num_words = 0;
size_t max_words = 0;
char *token;
char *delims = " \t\r\n";
while (fgets(buffer, sizeof buffer, fp) != NULL) {
token = strtok(buffer, delims);
while (token != NULL) {
if (strlen(token) == 4) {
++num_words;
if (num_words > max_words) {
max_words += ALLOC_INC;
char **temp = realloc(words, sizeof *temp * max_words);
if (temp == NULL) {
perror("Unable to allocate memory");
exit(EXIT_FAILURE);
}
words = temp;
}
words[num_words-1] = strdup(token);
}
token = strtok(NULL, delims);
}
}
if (fclose(fp) != 0) {
perror("Unable to close file");
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < num_words; i++) {
puts(words[i]);
}
/* Free allocated memory */
for (size_t i = 0; i < num_words; i++) {
free(words[i]);
}
free(words);
return 0;
}
Update
OP has mentioned that nonstandard functions are not permitted in solving this problem. Though strdup() is POSIX, and both common and standard in this sense, it is not always available. In such circumstances it is common to simply implement strdup(), as it is straightforward to do so. Here is the above code, modified so that now the function my_strdup() is used in place of strdup(). The code is unchanged, except that the feature test macro has been removed, the call to strdup() has been changed to my_strdup(), and of course now there is a function prototype and a definition for my_strdup():
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SZ 1000
#define ALLOC_INC 100
char * my_strdup(const char *);
int main(void)
{
FILE *fp = fopen("filename.txt", "r");
if (fp == NULL) {
perror("Unable to open file");
exit(EXIT_FAILURE);
}
char buffer[BUF_SZ];
char **words = NULL;
size_t num_words = 0;
size_t max_words = 0;
char *token;
char *delims = " \t\r\n";
while (fgets(buffer, sizeof buffer, fp) != NULL) {
token = strtok(buffer, delims);
while (token != NULL) {
if (strlen(token) == 4) {
++num_words;
if (num_words > max_words) {
max_words += ALLOC_INC;
char **temp = realloc(words, sizeof *temp * max_words);
if (temp == NULL) {
perror("Unable to allocate memory");
exit(EXIT_FAILURE);
}
words = temp;
}
words[num_words-1] = my_strdup(token);
}
token = strtok(NULL, delims);
}
}
if (fclose(fp) != 0) {
perror("Unable to close file");
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < num_words; i++) {
puts(words[i]);
}
/* Free allocated memory */
for (size_t i = 0; i < num_words; i++) {
free(words[i]);
}
free(words);
return 0;
}
char * my_strdup(const char *str)
{
size_t sz = strlen(str) + 1;
char *dup = malloc(sizeof *dup * sz);
if (dup) {
strcpy(dup, str);
}
return dup;
}
Final Update
OP had not posted code in the question when the above solution was written. The posted code does not compile as is. In addition to missing #includes and various syntax errors (extra braces, incorrect comment syntax) there are a couple of more significant issues. In func1(), the length variable is used uninitialized. This should be large enough so that inputWords[] can hold any expected word. Also, width specifiers should be used with %s in scanf() format strings to avoid buffer overflow. And, OP code should be checking whether the file opened successfully. Finally, func1() returns a value, but the calling function does not even assign this value to a variable.
To complete the task, the value returned from func1() should be used to declare a 2d array to store the four-letter words. The file can be rewound, but this time as fscanf() retrieves words in a loop, if a word has length 4, strcpy() is used to copy the word into the array.
Here is a modified version of OP's code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_WORD 100
int func1(FILE *pFile){
int counter = 0;
char inputWords[MAX_WORD];
while(fscanf(pFile,"%99s",inputWords) != EOF) {
if(strlen(inputWords) == 4) {
counter++;
}
}
return counter;
}
int main(void)
{
FILE *pFile = fopen("filename.txt","r");
if (pFile == NULL) {
perror("Unable to open file");
exit(EXIT_FAILURE);
}
char inputWords[MAX_WORD];
int num_4words = func1(pFile);
char words[num_4words][MAX_WORD];
int counter = 0;
rewind(pFile);
while(fscanf(pFile,"%99s",inputWords) != EOF) {
if(strlen(inputWords) == 4) {
strcpy(words[counter], inputWords);
counter++;
}
}
if (fclose(pFile) != 0) {
perror("Unable to close file");
}
for (int i = 0; i < num_4words; i++) {
puts(words[i]);
}
return 0;
}
I have this simple code to read the lines of a file and store them in a struct:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct filedata {
char **items;
int lines;
};
struct filedata *read_file(char *filename) {
FILE* file = fopen(filename, "r");
if (file == NULL) {
printf("Can't read %s \n", filename);
exit(1);
}
char rbuff;
int nlines = 0; // amount of lines
int chr = 0; // character count
int maxlen = 0; // max line length (to create optimal buffer)
int minlen = 2; // min line length (ignores empty lines with just \n, etc)
while ((rbuff = fgetc(file) - 0) != EOF) {
if (rbuff == '\n') {
if (chr > maxlen) {
maxlen = chr + 1;
}
if (chr > minlen) {
nlines++;
}
chr = 0;
}
else {
chr++;
}
}
struct filedata *rdata = malloc(sizeof(struct filedata));
rdata->lines = nlines;
printf("lines: %d\nmax string len: %d\n\n", nlines, maxlen);
rewind(file);
char *list[nlines];
int buffsize = maxlen * sizeof(char);
char buff[buffsize];
int i = 0;
while (fgets(buff, buffsize, file)) {
if (strlen(buff) > minlen) {
list[i] = malloc(strlen(buff) * sizeof(char) + 1);
strcpy(list[i], buff);
i++;
}
}
rdata->items = (char **)list;
fclose(file);
int c = 0;
for (c; c < rdata->lines; c++) {
printf("line %d: %s\n", c + 1, rdata->items[c]);
}
printf("\n");
return rdata;
}
int main(void) {
char fname[] = "test.txt";
struct filedata *ptr = read_file(fname);
int c = 0;
for (c; c < ptr->lines; c++) {
printf("line %d: %s\n", c + 1, ptr->items[c]);
}
return 0;
}
This is the output when I run it:
lines: 2
max string len: 6
line 1: hello
line 2: world
line 1: hello
line 2: H��
For some reason when it reaches the second index in ptr->items, it prints gibberish output. But yet, if I throw some printf()'s in there to show the pointer addresses, they're exactly the same.
Valgrind also prints this when iterating over the char array the second time:
==3777== Invalid read of size 8
==3777== at 0x400AB3: main (test.c:81)
==3777== Address 0xfff000540 is on thread 1's stack
==3777== 240 bytes below stack pointer
But that really doesn't give me any clues in this case.
I'm using gcc 4.9.4 with glibc-2.24 if that matters.
list is an non-static local variable and using it after exiting its scope (returning from read_file in this case) will invoke undefined behavior because it will vanish on exiting its scope. Allocate it dynamically (typically on the heap) like
char **list = malloc(sizeof(char*) * nlines);
Adding code to check if malloc()s are successful will make your code better.
The variable list is local to read_file, but you store a pointer to list in rdata->items. When read_file returns, rdata->items is a dangling pointer, and accessing it is undefined behavior.
I'd like to insert the result of the command system("echo %username%"); in a string, but I can't figure out how I could do it in C. Can someone please help me?
Adapted from this C++ solution and a little bit more flexible than August Karlstroms answer you can do something like this:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define S_SIZE 128
char * exec(const char* cmd)
{
FILE* pipe = _popen(cmd, "r"); // open a pipe to the command
if (!pipe) return NULL; // return on Error
char buffer[S_SIZE];
int size = S_SIZE;
char * result = NULL;
while (fgets(buffer, 128, pipe) != NULL)
{
result = realloc(result, size); // allocate or reallocate memory on the heap
if (result && size != S_SIZE) // check if an error occured or if this is the first iteration
strcat(result, buffer);
else if (result)
strcpy(result, buffer); // copy in the first iteration
else
{
_pclose(pipe);
return NULL; // return since reallocation has failed!
}
size += 128;
}
_pclose(pipe);
return result; // return a pointer to the result string
}
int main(void)
{
char* result = exec("echo %username%");
if (result) // check for errors
{
printf("%s", result); // print username
free(result); // free allocated string!
}
}
Use the POSIX function popen:
#include <stdio.h>
#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])
int main(void)
{
FILE *f;
char s[32];
const char *p;
f = popen("echo august", "r");
p = fgets(s, LEN(s), f);
if (p == NULL) {
s[0] = '\0';
}
pclose(f);
puts(s);
return 0;
}
int main()
{
//FILE *out = fopen("keimeno.txt", "w+");
FILE *in = fopen("keimeno.txt", "r");
int fullbufflen=0 , i;
char buffer[100];
fgets(buffer, 100, in);
int bufflen = strlen(buffer);
char *text;
text =calloc(bufflen,sizeof(char));
char *strcat(text, buffer);
// printf("line of \"keimeno.txt\": %s", buffer);
// printf("size of line \"keimeno.txt\": %i\n\n", bufflen);
fullbufflen = bufflen;
while(fgets(buffer, 100, in)!=NULL)
{
// printf("line of \"keimeno.txt\": %s", buffer);
// printf("size of line \"keimeno.txt\": %i\n\n", bufflen);
text =realloc(text,bufflen*sizeof(char));
char *strcat(text, buffer);
fullbufflen = bufflen + fullbufflen ;
}
for (i = 0;i<fullbufflen;i++)
{
printf("%c\n",text[i]);
}
}
I am trying to copy the full text file (keimeno.txt) into a dynamic memory array, with a buffer of 100 character at most everytime. To test it at the end i tried to prinf the results. And i just cant get it to work. Dont know if there is a problem on the printf at the end, or the whole program is just wrong.
Also the dynamic array is supposed to have 0 size at the beggining, so if someone could tell me how to do that too, it would be welcome.
Thanks in advance.
There are a few issues:
char *strcat(text, buffer);
This is not how you call a function. This is a function declaration, and an incorrect one at that since it doesn't define the types of the arguments.
To call strcat, just do this:
strcat(text, buffer);
Next, you're not allocating enough space for your buffers:
text =calloc(bufflen,sizeof(char));
You need to add space for the null bytes that terminates the string:
text =calloc(bufflen + 1,sizeof(char));
Similarly here:
text =realloc(text,bufflen*sizeof(char));
This only reallocates a total of bufflen bytes. It does not add bufflen bytes to what was already allocated, and bufflen is unchanged from when it was first set outside of the while loop. Do this instead:
bufflen = strlen(buffer);
text =realloc(text,bufflen+fullbufflen+1);
This gives you enough space for the current length, the additional buffer, and the null byte.
Finally, make sure you fclose(in) and free(text) at the end to clean up your resources, and be sure to check the return value of fopen to ensure that the file opened successfully, and realloc/calloc to ensure that your allocations worked.
After the above changes, you code should look like this:
int main(void)
{
//FILE *out = fopen("keimeno.txt", "w+");
FILE *in = fopen("keimeno.txt", "r");
if (in == NULL) {
perror("open failed");
exit(1);
}
int fullbufflen=0 , i;
char buffer[100];
fgets(buffer, 100, in);
int bufflen = strlen(buffer);
char *text;
text =calloc(bufflen+1,sizeof(char));
if (text == NULL) {
perror("calloc failed");
exit(1);
}
strcat(text, buffer);
// printf("line of \"keimeno.txt\": %s", buffer);
// printf("size of line \"keimeno.txt\": %i\n\n", bufflen);
fullbufflen = bufflen;
while(fgets(buffer, 100, in)!=NULL)
{
// printf("line of \"keimeno.txt\": %s", buffer);
// printf("size of line \"keimeno.txt\": %i\n\n", bufflen);
bufflen = strlen(buffer);
text =realloc(text,bufflen+fullbufflen+1);
if (text == NULL) {
perror("realloc failed");
exit(1);
}
strcat(text, buffer);
fullbufflen = bufflen + fullbufflen ;
}
fclose(in);
for (i = 0;i<fullbufflen;i++)
{
printf("%c\n",text[i]);
}
free(text);
}
Here is one possible solution:
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#define ERR (-1)
#define GROW 100
static void die ( const char * msg ) { perror(msg); exit(1); }
int main ( void ) {
char * filename = "in.txt";
size_t buffSize = 0;
size_t buffUsed = 0;
ssize_t bytesRead = 0;
char * buffer = NULL;
char * tmp;
int fd;
fd = open(filename, O_RDONLY, 0);
if (fd == ERR) die("open");
do {
buffUsed += (size_t)bytesRead;
if (buffUsed == buffSize) {
tmp = realloc(buffer, buffSize += GROW);
if (tmp == NULL) die("realloc");
buffer = tmp;
}
bytesRead = read(fd, buffer + buffUsed, buffSize - buffUsed);
if (bytesRead == ERR) die("read");
} while (bytesRead > 0);
if (write(STDOUT_FILENO, buffer, buffUsed) == ERR) die("write");
free(buffer);
if (close(fd) == ERR) die("close");
return 0;
}
Like the original, the input filename is hard-coded which sub-optimal...