I need to to open and read multiple text files which will be stored similar to binary search tree construction.
NOTE: They all need to be opened and their content stored before user enters his input.
I really do need some advice, because I do not see my errors any more.
However, what I really don't get, is how to make it interactive ?
When I press 'A', it needs to read text from the left node and opposite.
Structure of output:
------------------\n
\n
Title\n
file1 // file present - 'A'
file2 // file empty/not empty - 'B'\n
Text\n
\n
Your choice (A/B):\n
This is how the programs output should look at the beginning:
terminal
Elements of struct
typedef struct _Elements_
{
char* title_;
struct _Elements_* left;
struct _Elements_* right;
char* text_;
} Elements;
//Forward initialization is present
int main (int argc, char* argv[])
{
char returned_value;
Elements element;
if (argc != 2)
{
printf("Usage: ./ass2 [file-name]\n");
return 1;
}
returned_value = openFile(&element, argv[1]);
while(returned_value != ('A' || 'B'))
{
repeatEntry(returned_value); // Function to scan the value
}
// Tricky part! Read from left ???
if(returned_value == 'A')
{
printf("%s",element.left->text_);
}
else
{
printf("%s",element.right->text_);
}
return 0;
}
//Here i wanna initialize node elements
// For every malloc, I have to make sure that it succeeded
Elements* newNode(Elements* element)
{
Elements* newNode = (Elements*)malloc(sizeof(Elements));
if(newNode == NULL)
{
printf("[ERR] Out of memory.\n");
return (void*)2; // void* - to avoid warning of different type?
}
newNode->title_ = NULL;
newNode->left = NULL;
newNode->right = NULL;
newNode->text_ = NULL;
return newNode;
}
//Function that opens the files
char openFile(Elements* element, char* input)
{
// Create new memory if tree is empty
if(element == NULL)
{
return newNode(element);
}
FILE* file_open = fopen(input, "r");
if (file_open == NULL)
{
printf("[ERR] Could not read file %s.\n", input);
return 3;
}
// Local variables to handle the parsing file ?
char line[80];
int lenght_of_the_line; // Was just for me
int line_number = 0; // this also
// Do i really need local variables beside struct ?
char* title_local = NULL;
char* first_file = NULL;
char* second_file = NULL;
char* text_local = NULL;
while(fgets(line, 80, file_open))
{
lenght_of_the_line = strlen(line);
//printf("%d ", lenght_of_the_line);
//printf("%s ", line);
// Get title
if((line[lenght_of_the_line - 1] == '\n') && (line_number == 0))
{
title_local = (char*)malloc(lenght_of_the_line * (sizeof(char)) + 1);
if(title_local == NULL)
{
printf("[ERR] Out of memory.\n");
return 2;
}
strcpy(title_local, line);
printf("%s ", element->title_ = title_local);
printf("\n");
}
// Get 1st file name
else if( (line[lenght_of_the_line - 1] == '\n') && (line_number == 1) )
{
if( (line[lenght_of_the_line - 5] == '.') &&
(line[lenght_of_the_line - 4] == 't') &&
(line[lenght_of_the_line - 3] == 'x') &&
(line[lenght_of_the_line - 2] == 't') )
{
// Allocate enough memory for first file name
first_file = (char*)malloc(lenght_of_the_line * (sizeof(char)) + 1);
if(first_file == NULL)
{
printf("[ERR] Out of memory.\n");
return 2;
}
strcpy(first_file, line);
//name of file to open and store to left node
//Seems to work, since I got no error
openFile(element->left, first_file);
}
}
// Get 2nd file name
else if( (line[lenght_of_the_line - 1] == '\n') && (line_number == 2) )
{
if( (line[lenght_of_the_line - 5] == '.') &&
(line[lenght_of_the_line - 4] == 't') &&
(line[lenght_of_the_line - 3] == 'x') &&
(line[lenght_of_the_line - 2] == 't') )
{
second_file = (char*)malloc(lenght_of_the_line * (sizeof(char)) + 1);
if(second_file == NULL)
{
printf("[ERR] Out of memory.\n");
return 2;
}
strcpy(second_file, line);
openFile(element->right, second_file);
}
}
else
{
text_local = (char*)malloc(lenght_of_the_line * (sizeof(char)) + 1);
if(text_local == NULL)
{
printf("[ERR] Out of memory.\n");
return 2;
}
strcpy(text_local, line);
element->text_ = text_local;
printf("%s", element->text_);
}
// Increase line number for 1
line_number++;
}
//free(line);
fclose(file_open);
printf("\n");
printf("Your choice (A/B)? ");
char user_input;
scanf("%c", &user_input);
// DONT FORGET TO FREE THE MEMORY !
return user_input;
}
This
while(returned_value != ('A' || 'B'))
does not do what you probably expect. It compares the returned value to whatever value your compiler uses to represent true.
Currently your loop is hence likely to seem an endless loop, while waiting for returned_value to be identical to true.
You need
while((returned_value != 'A') && (returned_value != 'B'))
This will leave the loop when returned_value is identical to either 'A' or 'B'.
That way the endless loop problem is solved, which is likely the core problem preventing your file BST from being interactive.
By the way, for users convenience I recommend to also accept 'a' or 'b'.
For avoiding a "try again" after each input, use
scanf(" %c", &user_input);
with a space at the start, for ignoring leading white space, including the newline/return after each entered 'A' or 'B'.
Related
I recently started at university with C programming (beginner course), and now we are doing our final examination which is about a patients' database.
I'm required to read data from a text file to a struct array (size 10000). The file contains 2 string arrays (personal identification string (10 numbers seperated by a '-') and name string), 1 int array containing photo references and 1 integer containing the amount of photo references per patient. I have tried fscanf but the program just hangs whenever i try to read, when i use fgets, it reads the whole line and stores the integers from the photo reference array into my name array (middle one). I am wondering how I should go about doing this, I've spent days trying to figure out a solution but nothing seems to work. This is what my text file looks like:
123456-1234 Name Name [1, 2, 3, 4]
234567-2345 Name2 Name2 [1, 2]
345678-3456 Name3 Name3 []
And this is my write_to_file function which writes to the file when the program exits:
void write_to_file(Patient reg[], int *pNr_of_patients){
FILE *fp;
fp=fopen("file.txt","w");
if(*pNr_of_patients>0){
int i,j;
for(i=0;i<*pNr_of_patients;i++){
fprintf(fp,"%s\t%s\t[",reg[i].pers_nr,reg[i].name);
for(j=0;j<reg[i].nr_of_ref-1;j++){
fprintf(fp,"%d, ",reg[i].photo_ref[j]);
}
if(reg[i].photo_ref[j]==0){
fprintf(fp,"]");
}else{
fprintf(fp,"%d]",reg[i].photo_ref[j]);
}
fprintf(fp,"\n");
}
fclose(fp);
}
}
This is my read_from_file function, it's missing code for reading the int array values at the end:
Edit: I added a for loop to remove the characters starting at "[" from the name string, now i just need to know how to read the array values at the end into the struct's photo reference array.
void read_from_file(Patient reg[],int *pNr_of_patients){
FILE *fp;
fp=fopen("file.txt","r");
if(fp!=NULL){
reg[*pNr_of_patients].nr_of_ref=0;
int i=0, pos;
while(fgets(reg[*pNr_of_patients].pers_nr,13,fp)!=NULL){
reg[*pNr_of_patients].pers_nr[strlen(reg[*pNr_of_patients].pers_nr)-1]='\0';
fgets(reg[*pNr_of_patients].name,31,fp);
reg[*pNr_of_patients].name[strlen(reg[*pNr_of_patients].name)-1]='\0';
for(pos=0;pos<30;pos++){
if(reg[*pNr_of_patients].name[pos]=='['){
reg[*pNr_of_patients].name[pos]='\0';
}
}
(*pNr_of_patients)++;
}
fclose(fp);
}else{
printf("File does not exist\n");
}
}
This is what my Patient struct looks like:
struct patient{
char pers_nr[12], name[30];
int photo_ref[10], nr_of_ref;
};
typedef struct patient Patient;
Calling read_from_file in main:
int main(void){
Patient patient_register[10000];
int nr_of_patients=0;
read_from_file(patient_register,&nr_of_patients);
database_management(patient_register,&nr_of_patients); //this is where I fill all the data into the array before writing to the file at the end
write_to_file(patient_register,&nr_of_patients);
return 0;
}
I think that scanning input is one of the hardest in C. That's why libraries like cs50 exists, to ease up reading input for new C users. Anyway, I constructed my solution, but I redesigned your function.
The first solution reads a single Patient from a line. It does not use sscanf the only standard call that set's errno is to strtol, which is used to convert up numbers.
The second function uses sscanf and some crazy format string construction to stay safe of buffer overflow.
It all brings down at to how the input stream is constructed and how much you trust it.
#include <stdio.h>
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
struct patient{
char pers_nr[12];
char name[30];
int photo_ref[10];
size_t nr_of_ref;
};
typedef struct patient Patient;
int patient_read_from_line_1(const char line[], Patient *p)
{
assert(line != NULL);
assert(p != NULL);
// check the first 12 characters ----------
// first 6 chars must be numbers
for (int i = 0; i < 6; ++i) {
if (!isdigit(line[i])) {
return -__LINE__;
}
}
// followed by a single '-'
if (line[6] != '-') {
return -__LINE__;
}
// followed by 4 numbers
for (int i = 7; i < 7 + 4; ++i) {
if (!isdigit(line[i])) {
return -__LINE__;
}
}
// followed by a space
if (line[7 + 4] != ' ') {
return -__LINE__;
}
// read up first field ---------------------
// cool first field checks out
memcpy(p->pers_nr, line, 11);
p->pers_nr[11] = '\0';
line += 12;
// let's omit spaces
while (line[0] == ' ') {
line++;
}
// read up second field --------------------------
// now we should read a two strings separated by a space
// so we should read up until a second space
if (!isalpha(*line)) {
return -__LINE__;
}
const char *pnt_first_space = strchr(line, ' ');
if (pnt_first_space == NULL) {
return -__LINE__;
}
const char *pnt_another_space = strchr(pnt_first_space + 1, ' ');
if (pnt_another_space == NULL) {
return -__LINE__;
}
const size_t name_to_read_length = pnt_another_space - line;
if (name_to_read_length > sizeof(p->name)) {
return -__LINE__;
}
memcpy(p->name, line, name_to_read_length);
p->name[name_to_read_length] = '\0';
// buh two fields done, now the array
line += name_to_read_length;
// let's omit the spaces
while (line[0] == ' ') {
line++;
}
// read up array -----------------------------------
// array
if (line[0] != '[') {
return -__LINE__;
}
line++;
for (size_t numscnt = 0;; ++numscnt) {
if (numscnt >= sizeof(p->photo_ref)/sizeof(*p->photo_ref)) {
return -__LINE__;
}
char *pnt;
errno = 0;
long num = strtol(line, &pnt, 10);
if (errno) {
return -__LINE__;
}
if (!(INT_MIN < num && num < INT_MAX)) {
return -__LINE__;
}
p->photo_ref[numscnt] = num;
line = pnt;
// omit spaces
while (*line == ' ') line++;
// now we should get a comma
if (line[0] != ',') {
// if don't get a comma, we need to get a ]
if (line[0] == ']') {
// cool
++line;
// but remember to save the count
p->nr_of_ref = numscnt + 1;
// cool
break;
}
return -__LINE__;
}
++line;
// omit spaces
while (*line == ' ') line++;
// start again
}
// this needs to be end of line or newline
if (line[0] != '\0' && line[0] != '\n') {
return -__LINE__;
}
// success!
return 0;
}
// ok, ok, ok, let's use sscanf
int patient_read_from_line_2(const char line[], Patient *p)
{
assert(line != NULL);
assert(p != NULL);
int ret;
int pos;
// read up first fiedl and half of the second ------------------
ret = sscanf(line, "%12s %30[^ ] %n", p->pers_nr, p->name, &pos);
if (ret != 2) {
return -__LINE__;
}
line += pos;
// read up another half of the second field -------------------
const size_t cur_name_len = strlen(p->name);
p->name[cur_name_len] = ' ';
char tmp[20];
ret = snprintf(tmp, 20, "%%%d[^ ] [%%n", (int)(sizeof(p->name) - cur_name_len - 1));
if (ret < 0) {
return -__LINE__;
}
ret = sscanf(line, tmp, &p->name[cur_name_len + 1], &pos);
if (ret != 1) {
return -__LINE__;
}
line += pos;
// read up array *sigh* -------------------------------------------
for (p->nr_of_ref = 0;; ++p->nr_of_ref) {
if (p->nr_of_ref >= sizeof(p->photo_ref)/sizeof(*p->photo_ref)) {
return -__LINE__;
}
ret = sscanf(line, " %d%1s%n", &p->photo_ref[p->nr_of_ref], tmp, &pos);
if (ret == 0) {
// hm...
if (line[0] == ']') {
// ach all ok, empty numbers list;
line++;
p->nr_of_ref++;
break;
}
return -__LINE__;
}
if (ret != 2) {
return -__LINE__;
}
line += pos;
if (tmp[0] != ',') {
if (tmp[0] == ']') {
// whoa! success
p->nr_of_ref++;
// cool
break;
}
return -__LINE__;
}
}
// so what's left? - EOF or newline
if (line[0] != '\0' && line[0] != '\n') {
return -__LINE__;
}
// success!
return 0;
}
long patient_read_from_file(FILE *fp, Patient patients[], size_t patients_len)
{
size_t patients_cnt = 0;
char line[256];
// for each line in file
while (fgets(line, sizeof(line), fp) != NULL) {
const int ret = patient_read_from_line_2(line, &patients[patients_cnt]);
if (ret < 0) {
// hanle reading error
return ret;
}
patients_cnt++;
if (patients_cnt > patients_len) {
// no more memory in patients left
return -__LINE__;
}
}
return patients_cnt;
}
void patient_fprintln(FILE *f, const Patient *p)
{
fprintf(f, "%s %s [", p->pers_nr, p->name);
for (size_t i = 0; i < p->nr_of_ref; ++i) {
fprintf(f, "%d", p->photo_ref[i]);
if (i + 1 != p->nr_of_ref) {
fprintf(f, ",");
}
}
fprintf(f, "]\n");
}
int main()
{
FILE *fp;
fp = stdin; // fopen("file.txt","r");
if (fp == NULL) {
return -__LINE__;
}
Patient patients[3];
const long patients_cnt = patient_read_from_file(fp, patients, sizeof(patients)/sizeof(*patients));
if (patients_cnt < 0) {
fprintf(stderr, "patient_read_from_file error %ld\n", patients_cnt);
return patients_cnt;
}
fclose(fp);
printf("Readed %d patients:\n", patients_cnt);
for (size_t i = 0; i < patients_cnt; ++i) {
patient_fprintln(stdout, &patients[i]);
}
return 0;
}
Live version available at onlinedbg.
This can be simplified for 100%. This has bugs for 100%. It is just to show what methods (strtol, memcpy, sscanf, isdigit, isalpha) are sometimes used by people to read from input. Also I specify length modifier to scanf (sscanf(..., "%12s") to handle overflows (hopefully). Try to always check return values from scanf and other standard functions (maybe checking snprintf return value is a little too much, but hey, let's be consistent). Be vary, that on some platforms the %n scanf modifier happens not to work. Also this can be build up to use dynamic allocation using malloc, realloc and free, both on line reading (basically it is equal to writing custom version of GNU getline), reading strings from input, reading int's array from input and dynamic allocations of patients.
This was meant as a comment but got too long, so I type it here.
read_from_file() appears overly complex. You might consider revisiting fscanf, reading the photo references as a whole string and then parsing into integers which you can assign to the photo_ref array. (While the code below might compile, I haven't verified that it works. It's just an idea of how one might proceed.)
void read_from_file (Patient reg[], int *pNr_of_patients)
{
FILE *fp;
fp = fopen ("file.txt", "r");
if (fp != NULL)
{
int n;
int i = 0; // position in photo_ref
char refs[30];
*pNr_of_patients = 0;
while (EOF !=
(n =
fscanf (fp, "%s %[^[]%[^]]]", reg[*pNr_of_patients].pers_nr,
reg[*pNr_of_patients].name, refs)))
{
// btw, reg[*pNr_of_patients].name may contain terminating blanks. right trim it. that's easy enough.
if (n > 2)
{ /* found photo refs.Now split the string into integers */
char *s = refs + 1; //skip '['
char *p;
while (*s && i<10){ // scan for the integers, 10 of them
while (*s && *s == ' ')
s++; // skip blanks
p = s; // mark start of number
while (*p && *p != ',')
p++;
if (*p == ',')
*p = 0;
reg[*pNr_of_patients].photo_ref[i++] = atoi (s); //tip: use strtol(3), verify that `i' isnt larger than size of the array
s = p + 1; // skip ','. Must Do: verify that `s' hasnt yet moved past the end of `ref'!!
}
}
(*pNr_of_patients)++;
}
fclose (fp);
}
else
{
printf ("File does not exist\n");
}
}
There are some good answers already, but most of them try to use a single method to parse all elements of the line. I would read whole lines into a buffer first, then use sscanf() to parse the patient number and name, but use strtok() to split the array into its individual components:
void read_from_file(Patient reg[], int *pNr_of_patients) {
FILE *fp = fopen("file.txt", "r");
if (!fp) {
fprintf(stderr, "Error opening file: %s\n", strerror(errno));
*pNr_of_patients = 0;
return;
}
char line[1024];
int i = 0;
while (fgets(line, sizeof line, fp)) {
int offset = 0;
int refs = 0;
sscanf(line, "%11s %29[^[] [%n", ®[i].pers_nr, ®[i].name, &offset);
for (char *tok = strtok(line + offset, ","); tok && refs < 10; tok = strtok(NULL, ",")) {
if (*tok != ']')
reg[i].photo_ref[refs++] = atoi(tok);
}
reg[i].nr_of_ref = refs;
i++;
}
*pNr_of_patients = i;
}
Divide and Conquer
Break this down into steps. Make a function that populates 1 Patient.
The below is untested code. Consider it a starting point. The deign goal is to make a function that reads 1 line into 1 Patient.
Read in 1 entire line
// return 1: success, 0: failure EOF:end-of-file
int read_once_from_file(FILE *stream, Patient *pat_ptr) {
Patient pat = { 0 };
char buffer[100 + 30*13];
if (fgets(buffer, sizeof buffer, stream) == NULL) {
return EOF;
}
Parse the first part. Use "%n" which records the parsing offset. Use width limits on string input.
int n = 0;
if (sscanf(buffer, " %11[^\t] %29[^\t] [ %n", pat.pers_nr, pat.name) != 2) {
return 0; // improper formatted input
}
char *p = buffer + n;
Now look for ']' and photo_ref
if (*p != ']') {
for (pat.nr_of_ref=0; ; pat.nr_of_ref++) {
if (sscanf(p, "%d %n", &pat.photo_ref[i], &n) != 1) {
return 0; // improper formatted input
}
p += n;
if (*p == ']') {
pat.nr_of_ref++;
break;
}
if (*p != ',' || pat.nr_of_ref + 1 == 10) {
return 0; // improper formatted input
}
p++;
}
}
Save result
*pat_ptr = pat;
return 1;
}
Call read_once_from_file() as needed
void read_from_file(Patient reg[],int *pNr_of_patients){
*pNr_of_patients = 0;
FILE *fp = fopen("file.txt","r");
if(fp){
for (int i = 0; i<10000; i++) {
int count = read_once_from_file(fp, ®[i]);
if (count == EOF) {
break;
}
if (count != 1) {
// error
fprintf(stderr, "Input error\n");
break;
}
}
*pNr_of_patients = i;
fclose(fp);
}
}
So I'm looping through readdir() function calls and adding the resulting file name to a new node in a linked list. After fixing an issue by setting file_list = add_file_node(), I'm running into and issue where the dir_list loop is having problems accessing the directory.
hls: cannot access hls: cannot access h: No such file or directory
code:
#include "header.h"
/**
* main - main ls function
*
* #argc: argument count
* #argv: argument vector
*
* Return: 0, or the errno of the error
*/
int main(int argc, char **argv)
{
struct dirent *read;
char dir[400], error_message[400], format, hidden;
int i, j, dir_count, max_src_bytes = 397;
dir_list_t *dir_list, *dir_node;
file_list_t *file_list;
DIR *dirp;
int errno;
format = ' ';
hidden = ' ';
dir_count = 0;
strcpy(dir, ".");
for (i = 1; i < argc; i++)
{
if (argv[i][0] == '-')
{
for (j = 1; argv[i][j]; j++)
{
if (argv[i][j] == '1')
format = '1';
else if (argv[i][j] == 'l')
format = 'l';
if (argv[i][j] == 'a')
hidden = 'a';
else if (argv[i][j] == 'A')
hidden = 'A';
}
}
else
{
memset(dir, 0, strlen(dir));
strcpy(dir, argv[i]);
dir_list = add_dir_list(&dir_list, dir);
dir_count++;
}
}
if (dir_count == 0)
dir_list = add_dir_list(&dir_list, dir);
for (dir_node = dir_list; dir_node != NULL; dir_node = dir_node->next)
{
dirp = opendir(dir_node->dir);
if (dirp == NULL)
{
strcpy(error_message, "hls: cannot access ");
max_src_bytes = 381;
perror(strncat(error_message, dir_node->dir, max_src_bytes));
return (errno);
}
if (dir_count > 1)
printf("%s:\n", dir_node->dir);
while ((read = readdir(dirp)) != NULL)
{
file_list = add_file_list(&file_list, read->d_name);
}
switch (format)
{
case '1':
print_ls(hidden, '\n', file_list);
break;
case 'l':
print_ls(hidden, '\n', file_list);
break;
default:
print_ls(hidden, '\t', file_list);
}
if (dir_node->next != NULL)
putchar('\n');
free_file_list(&file_list);
}
free_dir_list(&dir_list);
closedir(dirp);
return (0);
}
/**
* print_ls - print contents in the default ls format, i.e. columns
*
* #hidden: parameter denoting the option for revealing hidden files
* #format: printing format parameter
* #dirp: pointer to the directory data
*
* Return: 0 for success, 1 for failure
*/
int print_ls(char hidden, char format, file_list_t *file_list)
{
file_list_t *file_node;
for (file_node = file_list; file_node != NULL; file_node = file_node->next)
{
if (hidden == 'a')
{
printf("%s", file_list->file);
if (file_list->next != NULL)
putchar(format);
}
else if (hidden == 'A')
{
if (strcmp(file_list->file, ".") != 0 &&
strcmp(file_list->file, "..") != 0)
{
printf("%s", file_list->file);
if (file_list->next != NULL)
putchar(format);
}
}
else
{
if (file_list->file[0] != '.')
{
printf("%s", file_list->file); // (line 139)
if (file_list->next != NULL)
putchar(format);
}
}
}
if (format == '\t')
printf("\n");
return (0);
}
add_file_list():
/**
* add_file_list - add a new node at the start of a file_list_t linked list
*
* #head: start of linked list
* #file: file data to add to node
*
* Return: address of new node; NULL if failure
*/
file_list_t *add_file_list(file_list_t **head, const char file[256])
{
file_list_t *node;
node = malloc(sizeof(file_list_t));
if (node == NULL)
return (NULL);
strcpy(node->file, file);
node->next = *head;
node->prev = NULL;
*head = node;
return (node);
}
I'm thinking about trying this out with an array of pointers instead, but I don't want to throw away my code before getting some insight. Am I not inputting the data into the node correctly? If so, how would I do that?
this is wrong, just as valgrind says
file_head = file_list; <<<< file_list is not initlaized, file_head = junk
while ((read = readdir(dirp)) != NULL)
{
printf("read: %s\n", read->d_name);
append_file_list(&file_list, read->d_name);
printf("file_list: %s\n", file_list->file);
}
printf("file_head: %s\n", file_head->file); <<<<<= (line 78) file_head = junk
I'm getting the following error when freeing "shifted_text" below. I've checked with print statements and commenting things out, and it's definitely that free(shifted_text). The other free commands are working fine.
Debug Error!
HEAP CORRUPTION DETECTED: After normal block (#77) at 0x007D1F...
CRT detected that the application wrote to memory after end of heap buffer.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void parse(int argc, char *argv[]);
char * shift_text(char *sometext);
char shift_letter(char letter, char shift);
void bring_in_text(void);
void write_to_file(char *sometext);
char *flag;
char *password;
char *text;
int main(int argc, char *argv[])
{
parse(argc, argv);
char *shifted_text;
// at this point flag can only be -e, -d, -df, -ef
if (strcmp(flag, "-e") == 0 || strcmp(flag, "-d") == 0)
{
// location to store the shifted text
shifted_text = (char*)malloc(strlen(text) * sizeof(char));
shift_text(shifted_text);
printf("%s\n", shifted_text);
}
else
{
bring_in_text();
// location to store the shifted text
shifted_text = (char*)malloc(strlen(text) * sizeof(char));
shift_text(shifted_text);
write_to_file(shifted_text);
}
free(shifted_text);
free(text);
free(flag);
free(password);
return 0;
}
void write_to_file(char *sometext)
{
if (strcmp(flag, "-df") == 0)
{
FILE *fp;
fp = fopen("plaintext.txt", "w");
if (fp == NULL)
{
puts("Unable to open file");
exit(1);
}
fprintf(fp, sometext);
fclose(fp);
}
else if (strcmp(flag, "-ef") == 0)
{
FILE *fp;
fp = fopen("ciphertext.txt", "w");
if (fp == NULL)
{
puts("Unable to open file");
exit(1);
}
fprintf(fp, sometext);
fclose(fp);
}
}
void bring_in_text(void)
{
if (strcmp(flag, "-df") == 0)
{
FILE *fp;
fp = fopen("ciphertext.txt", "r");
if (fp == NULL)
{
puts("Unable to open file");
exit(1);
}
while (!feof(fp))
{
text = (char*)malloc(100 * sizeof(char));
fgets(text, 100, fp);
}
fclose(fp);
}
else if (strcmp(flag, "-ef") == 0)
{
FILE *fp;
fp = fopen("plaintext.txt", "r");
if (fp == NULL)
{
puts("Unable to open file");
exit(1);
}
while (!feof(fp))
{
text = (char*)malloc(100 * sizeof(char));
fgets(text, 100, fp);
}
fclose(fp);
}
}
char * shift_text(char *shifted_text)
{
char *temptext;
temptext = text;
char *tempshiftedtext;
tempshiftedtext = shifted_text;
// space for 10 characters plus null
char *temppswd;
temppswd = password;
for (int i = 0; i < strlen(text); i++)
{
char a;
if (*temptext >= 97 && *temptext <= 122)
{
a = shift_letter(*(temptext + i), *(temppswd + (i % strlen(password))));
*(tempshiftedtext + i) = a;
}
else
*(tempshiftedtext + i) = *(temptext + i);
}
*(tempshiftedtext + strlen(text)) = '\0';
}
char shift_letter(char letter, char shift)
{
if (strcmp(flag, "-e") == 0 || strcmp(flag, "-ef") == 0)
{
letter = letter - 97;
shift = shift - 97;
int shifted_letter = letter + shift;
if (shifted_letter > 25)
shifted_letter %= 26;
shifted_letter += 97;
return (char)shifted_letter;
}
else if (strcmp(flag, "-d") == 0 || strcmp(flag, "-df") == 0)
{
int shifted_letter = letter - 97;
shift = shift - 97;
int letter = shifted_letter - shift;
letter %= 26; // mod seems to allow negative results, so if its still negative. add another val equal to modulus
if (letter < 0)
letter += 26;
letter += 97;
return (char)letter;
}
}
void parse(int argc, char *argv[])
{
if (argc == 4)
{
// internally calls malloc on strlen(argv[i])
flag = _strdup(argv[1]);
password = _strdup(argv[2]);
text = _strdup(argv[3]);
if (strlen(password) > 10)
{
puts("Password too long");
exit(1);
}
else if (strcmp(flag, "-e") != 0 && strcmp(flag, "-d") != 0)
{
puts("Incorrect flag");
exit(1);
}
}
else if (argc == 3)
{
// internally calls malloc on strlen(argv[i])
flag = _strdup(argv[1]);
password = _strdup(argv[2]);
if (strlen(password) > 10)
{
puts("Password too long");
exit(1);
}
else if (strcmp(flag, "-ef") != 0 && strcmp(flag, "-df") != 0)
{
puts("Incorrect flag");
exit(1);
}
}
else
{
puts("Incorrect arguements");
exit(1);
}
}
The functions parse simply stores command line arguments in the global's. The shifting functions shift a letter by some number. 'A' shifted by 2 would be 'C' for example. These work fine and without the free(shifted_text) the program works.
I'm new to C so it's probably something simple but I can't see it.
Change this
shifted_text = (char*)malloc(strlen(text) * sizeof(char));
to
shifted_text = malloc((strlen(text) + 1) * sizeof(char)); // don't cast
A C-style string always has a null-terminator, indicating the end of the string. For example, "foo" is stored as 'f', 'o', 'o', '\0' in memory. So you have to
I suspect that the heap buffer corruption isn't caused by your free(shifted_text);. Since insufficient memory is allocated to shifted_text, undefined behaviour is invoked, making everything possible. So your program may either run properly or crash. Perhaps it's only a coincidence that every time free(shifted_text); is commented out, your program runs correctly thanks to the undefined behaviour.
BTW: There are many places in your code to be refined. For example, in void bring_in_text(void):
while (!feof(fp))
{
text = (char*)malloc(100 * sizeof(char));
fgets(text, 100, fp);
}
Covering the previous lines without even processing them? Also, text isn't freed in this function.
strdup allocates strlen+1 chars and you only allocate strlen chars. When you write the null at the end of shifted text you are overflowing the buffer.
I've got a problem with reading words from file and passing it to binary tree. When I debug it, it says:
Unhandled exception at 0x76E7773B(ntdll.dll) in Projekt.exe: 0.C00000005:
Access violation reading location 0x0037902A.
Here is the source code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
typedef struct Tree {
int val;
char *word;
struct Tree *left;
struct Tree *right;
} Tree;
void show(Tree *hd) {
if (hd != NULL) {
show(hd->left);
show(hd->right);
printf("%s -- %d\n", hd->word, hd->val);
}
}
void zero(Tree *aTree) {
if (aTree == NULL)
return;
zero(aTree->left);
free(aTree);
zero(aTree->right);
}
int alpha(char *word1, char *word2) {
if (word1[0] == 0 && word2[0] == 0)
return 2;
else
if (word1[0] == word2[0])
return alpha(&word1[1], &word2[1]);
else
if (word1[0] < word2[0])
return 1;
else
return 0;
}
Tree *create(char *word) {
Tree *temp;
temp = (Tree*)malloc(sizeof(Tree));
temp->left = temp->right = NULL;
temp->val = 1;
temp->word = (char*)malloc(sizeof(char));
strcpy(temp->word, word);
return temp;
}
Tree *insert(Tree *aTree, char *word) {
if (aTree == NULL) {
aTree = create(word);
} else
if (alpha(aTree->word, word) == 0) {
aTree->left = insert(aTree->left,word);
} else
if (alpha(aTree->word, word) == 1) {
aTree->right = insert(aTree->right, word);
} else
if (alpha(aTree->word, word) == 2) {
aTree->val++;
}
return aTree;
}
int main(int argc, char *argv[]) {
Tree *myTree = NULL;
char buffer[256] = { 0 };
char temp = 0;
int i = 0;
FILE *fp = fopen(argv[1], "r");
if (fp) {
while (temp != EOF) {
temp = getc(fp);
temp = toupper(temp);
if (temp >= 65 && temp <= 90) {
buffer[i] = temp;
i++;
} else {
if (buffer[0] != 0) {
puts(buffer);
myTree = insert(myTree, buffer);
memset(buffer, 0, sizeof(buffer));
i = 0;
}
}
}
}
fclose(fp);
show(myTree);
return 0;
}
Your program has several problems:
in function zero, you free the pointer too soon, you should move the free(aTree); as the last statement, otherwise you invoke undefined behavior, possibly a crash (but not the one you have, since you never call this function):
void zero(Tree *aTree) {
if (aTree != NULL) {
zero(aTree->left);
zero(aTree->right);
free(aTree);
}
In function alpha, you use recursion where a simple loop would suffice. The compiler may convert this to a loop, but it does have to. This is not a bug but why not use a more idiomatic approach such as:
int alpha(const char *word1, const char *word2) {
for (size_t i = 0;; i++) {
if (word1[i] == '\0' && word2[i] == '\0')
return 2;
if (word1[i] == word2[i])
continue;
if (word1[i] < word2[i])
return 1;
else
return 0;
}
}
In function create, you allocate a single byte for the string, this is definitely a cause for the crash. You should allocate strlen(word) + 1 or use strdup(word). You should not cast the return value of malloc() either:
Tree *create(const char *word) {
Tree *temp;
temp = malloc(sizeof(Tree));
temp->left = temp->right = NULL;
temp->val = 1;
temp->word = strdup(word);
return temp;
}
In function insert you call alpha multiple times, this is inefficient: you could use a switch statement:
Tree *insert(Tree *aTree, const char *word) {
if (aTree == NULL) {
return create(word);
switch (alpha(aTree->word, word)) {
case 0:
aTree->left = insert(aTree->left, word);
break;
case 1:
aTree->right = insert(aTree->right, word);
break;
case 2:
aTree->val++;
break;
}
}
return aTree;
}
function main has multiple issues:
You do not check if argv[1] is provided to the program. It would be NULL if the program is run without a command line argument.
Your test for end of file is incorrect: temp should be defined as int and you should test its value after reading the byte from the file with getc(), it is idiomatic to name c a variable used for this.
You should use character literals instead of hard coded ASCII values.
the test if (c >= 'A' && c <= 'Z') would work for ASCII, which is almost universal today, but it is more reliable to use isupper(c) instead.
You do not need to clear the buffer, setting a '\0' at the end before inserting the word is enough.
You should also check for buffer overflow and refuse to handle words longer than 255 characters.
You should not call fclose(fp) when fp is NULL, this is undefined behavior.
Here is a corrected version:
int main(int argc, char *argv[]) {
Tree *myTree = NULL;
char buffer[256];
int c;
size_t i;
FILE *fp;
if (argc < 2) {
printf("missing argument\n");
return 2;
}
fp = fopen(argv[1], "r");
if (fp == NULL) {
printf("cannot open %s\n", argv[1]);
return 1;
}
i = 0;
while ((c = getc(fp)) != EOF) {
c = toupper(c);
if (isupper(c)) {
if (i < sizeof(buffer))
buffer[i] = c;
i++;
} else {
if (i > 0 && i < sizeof(buffer)) {
buffer[i] = '\0';
puts(buffer);
myTree = insert(myTree, buffer);
i = 0;
}
}
}
fclose(fp);
show(myTree);
return 0;
}
I am trying to make word search using fgetc. I understand what fgetc does but i am getting seg fault. on running the gdb test, i returns the following. Is there an easier way to implement the search function?? i am new to programming.
thank you for the help.
#0 0x00007ffff7aa4c64 in getc () from /lib64/libc.so.6
#1 0x000000000040070c in main ()
Where am i going wrong?
#include <stdio.h>
#include <stdlib.h>
int isAlpha(char c)
{
if( c >= 'A' && c <='Z' || c >= 'a' && c <='z' || c >= '0' && c <= '9' )
{
return 1;
}
else
{
return 0;
}
}
int CheckFunctionn(int length, int message_counter, char ref_word[], char newmessage[])
{
int newCounter = 0;
int counterSuccess = 0;
while(newCounter < length)
{
if(ref_word[newCounter] == newmessage[newCounter + message_counter])
{
counterSuccess++;
}
newCounter++;
}
if(counterSuccess == length)
{
return 1;
}
else
{
return 0;
}
}
int main(int argc, char *argv[])
{
char message[300];
int counter = 0;
int ref_length = 0;
int alphaCounter = 0;
int alphaCounterTime = 0;
int messageCounter = 0;
int word_counter = 0;
FILE* input;
FILE* output;
//long fileLength;
//int bufferLength;
//char readFile;
//int forkValue;
input = fopen(argv[2],"r");
output = fopen(argv[3],"w");
int c;
c = fgetc(input);
while(c != EOF)
{
while((argv[1])[ref_length] !='\0')
{
// if string is "HEY", (argv[1]) is HEY, ref_counter is the length
// which in this case will be 3.
ref_length++; //<-- takes care of the length.
}
while(alphaCounter < ref_length)
{
// this will add to alphaCounter everyetime alphaCT is success.
alphaCounterTime += isAlpha((argv[1])[alphaCounter]);
alphaCounter++;
}
if(alphaCounterTime != ref_length)
{
return 0;
}
if((messageCounter == 0 ) && (message[messageCounter + ref_length] == ' ' || message[messageCounter] == '\n' || message[messageCounter]== '\t')) // counts the whole things and brings me to space
{
// compare the message with the word
word_counter += CheckFunctionn(ref_length, messageCounter, argv[1], message);
}
if((message[messageCounter] == ' ' || message[messageCounter] == '\n' || message[messageCounter]== '\t') && (message[messageCounter + ref_length + 1] == ' ' || message[messageCounter + ref_length + 1] == '\n' || message[messageCounter + ref_length + 1]== '\t'))
{
word_counter += CheckFunctionn(ref_length, messageCounter + 1, argv[1], message);
}
if((message[messageCounter]== ' '|| message[messageCounter] == '\n' || message[messageCounter]== '\t') && (messageCounter + ref_length+1)== counter) //<-- this means the length of the message is same
{
word_counter += CheckFunctionn(ref_length, messageCounter + 1, argv[1], message);
}
messageCounter++;
}
fclose(input);
fclose(output);
return 0;
}
You're almost certainly failing to open the input file. If fopen fails, it returns NULL, and calling fgetc(NULL) has undefined behavior, and a segmentation fault is one possible outcome of undefined behavior.
You need to check for errors and handle then accordingly. You also need to check if your program was given sufficient arguments. Here's one way to handle them:
if (argc < 3)
{
fprintf(stderr, "Usage: %s input-file output-file\n", argv[0]);
exit(1);
}
input = fopen(argv[1],"r");
if (input == NULL)
{
fprintf(stderr, "Error opening input file %s: %s\n", argv[1], strerror(errno));
exit(1);
}
output = fopen(argv[2],"w");
if (output == NULL)
{
fprintf(stderr, "Error opening output file %s: %s\n", argv[2], strerror(errno));
exit(1);
}
You only read one character into c, then loop while(c != EOF) which is almost always an infinite loop. Inside that loop, you increment messageCounter which you use to walk past the end of an array -- boom!
Per your comment, argc is 2, but you refer to argv[2] which would be the third element of the args, and will be NULL. The FILE * is going to end up being NULL too (because it's invalid to pass NULL to fopen).
It will be very easy if you use strcmp function in this...
What you have to do is first find the length of ur file using ftell and after that allocate that much memory then fill that memory using fgetc or fgets or any other file function...then just use strcmp function on that....bingo!!!!! :)