Suppose I want to "reuse" an array of char pointers, like in the following program that loops files given in the argument list, loops the lines in the file, adds them to a dynamically allocated array, then prints it:
// includes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// globals
int progReturn = 0;
int globalLineCounter = 0;
////// main
int main(int argc, char *argv[]) {
FILE *fp;
int i;
// iterate files. first arg is the program name
for (i = 1; i < argc; i++) {
fp = fopen(argv[i], "r");
if (fp == NULL) {
fprintf(stderr, "The file '%s' did not exist.\n", argv[i]);
progReturn = 1;
} else {
// read lines from the file
char line[256];
// THE PROBLEM: I'd like to completely clear this array.
char **lines = malloc(16 * sizeof(char*));
// iterate lines
int fileLineCounter = 0;
while (fgets(line, sizeof(line), fp)) {
// remove newline
strtok(line, "\n");
// add lines to array
lines[globalLineCounter] = malloc(256 * sizeof(char));
strcpy(lines[globalLineCounter], line);
//printf("%s\n", lines[globalLineCounter]); // tester
fileLineCounter++;
globalLineCounter++;
}
// all lines read
printf("The file '%s' had %d lines.\n", argv[i], fileLineCounter);
// print the array
int j=0;
for (j=0; j<fileLineCounter; j++) {
// PROBLEM: Garbage from the second file when it prints here.
printf("%s\n", lines[j]);
}
// delete lines, delete file
memset(lines, 0, sizeof(*lines));
fclose(fp);
}
}
// all files read
return progReturn;
}
On the first file, everything works with no problems. On the second file, when I print the array, It shows unprintable characters, and some of the lines from the first file.
What could be causing this issue? Am I not fully clearing **lines?
EDIT: example input and output:
input file foo:
This is a test
of the lineSort program
in order to
test its capabilities.
Lots of whitespace too!
aaa
bbb
cccccc
aaa
ggggg
hhhhh
fffff
eeeee
ddddd
ppppp
input file bar:
aaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbb
zzzzzzzzzzzzzzzzz
ccccccccccccccccc
yyyyyyyyyyyyyyyyy
output for sortLine foo bar:
The file 'foo' had 20 lines.
cccccc
Lots of whitespace too!
This is a test
aaa
aaa
bbb
ddddd
eeeee
fffff
ggggg
hhhhh
in order to
of the lineSort program
ppppp
test its capabilities.
The file 'bar' had 5 lines.
(x▒▒
(x▒▒
Lots of whitespace too!
in order to
test its capabilities.
strcpy(lines[globalLineCounter], line);
This looks like your main problem. globalLineCounter keeps increasing across all input files.
Let's say your first input file contains 10 lines and your second file 5 lines. Then your code will create a dynamic array (of dynamic arrays) and store the lines from the first file in elements 0 .. 9 (and then print them). You never free any of the allocated memory, so it all leaks at the end of the loop.
For the second file, you create another dynamic array. You store the 5 lines from the second file in elements 10 .. 14 (via globalLineCounter), but then print elements 0 .. 4 (fileLineCounter). Those elements are uninitialized and contain garbage.
Move the char **lines initialization outside of the for loop.
Rename the index counter i to something different.
Repeatedly calling lines[i] = malloc(...) on multiple files will cause a memory leak. Think about using free inside the for loop, or move this part of the initialization outside the for loop.
Related
I am writing a program that inputs two text files
inputtxt1,
inputtxt2
and output
outputtxt file
In these two files information such as
input txt1
S00111111 5 6-Jul-19 09-Aug-19
S00800000 4 1-Jul-19 30-Aug-19
S00000000 1 1-Jul-19 30-Aug-19
input txt2
S00111111 3 6-Jul-19 09-Aug-19
S00222222 1 20-Jul-19 30-Aug-19
S00000000 1 1-Jul-19 30-Aug-19
I am writing a program to input these two txt files and output the differences in SQL queries and the values inside the bracket will change depends on the differences from these text files.
DELETE FROM TABLE WHERE TABLE=[] AND TABLE=[]
INSERT INTO TABLE (TABLE1,TABLE2,TABLE3,TABLE4) VALUES ([ ],[],'[2019-08-30] 00:00:00','[2019-07-01] 00:00:00');
DELETE FROM TABLE WHERE TABLE=[] AND TABLE=[4]
INSERT INTO TABLE (TABLE,TABLE) VALUES ([],[4]);
I wrote my draft in C so what I did id basically a while loop to read each of the line of the first file and each of the line of the second file and output the query.
Here are my two questions:
First it, unfortunately, output the file SQL 3 times, I think there is something wrong with my while loop.
Secondly, how would I make the program detect that specific character from specific line need to be printed in the query for example number 5 in the first line would detect and add to the value of one of the tables in the query.
/* This program will input two text files, output a text file with the differences*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
FILE *in1, *in2, *out;
int a, b;
void main (void)
{
int c;
char* singleline [33];
char* singleline2 [33];
in1 = fopen ("inputtest1.txt", "r"); /* reads from the first file */
in2 = fopen ("inputtest2.txt", "r"); /* reads from the second file */
out = fopen ("outputtest3", "w"); /* writes out put to this file */
// Menu //
printf ("TSC Support Program\n\n");
printf ("This program compare the two files and output the differences in SQL quries \n");
// if the file is empty or something went wrong!
if (in1 == NULL || in2 == NULL)
{
printf("********************Can Not Read File !**************************");
}
else
{
// Checking Every line in the first text file if it equals to the first line of the text file
while (!feof(in1)&&!feof(in2))
{
// a = getc(in1);
// b = getc(in2);
a = fgets(singleline, 33,in1);
b = fgets(singleline2, 33,in2);
if (a!=b)
{
printf("\n SQL will be printed\n");
fprintf (out,
"\n DELETE FROM BADGELINK WHERE BADGEKEY=[27] AND ACCLVLID=75"
"\nINSERT INTO BADGELINK (BADGEKEY,ACCLVLID,ACTIVATE,DEACTIVATE) VALUES ([27],75,'[2010-08-24] 00:00:00','[2010-12-17] 00:00:00'); \n"
"\n DELETE FROM BADGE WHERE BADGEKEY=[27] AND ISSUECODE=[75]"
"\nINSERT INTO BADGE (BADGEKEY,ISSUECODE) VALUES ([27],[1]);\n"
);
}
else
{
printf("Something went wrong");
}
}
}
fclose(in1);
fclose(in2);
fclose(out);
}
It prints the output 5 times
and then it says something went wrong. I am unsure what went wrong.
if (a != b) does not do what you think it is doing. Check strncmp() or memcmp() library functions.
But if you want to find out the first different character in two strings, the code below would do it for you.
Not tested properly, so take it as a quick prototype.
#include <stdio.h>
int strdiff(char *s1, char *s2){
char *p1 = s1;
while(*s1++ == *s2++)
;
if (s1 != s2)
return --s1-p1; /* we have s1++ in the while loop */
return -1;
}
int main(){
char *s1="S00111111 5 6-Jul-19 09-Aug-19";
char *s2="S00111111 3 6-Jul-19 09-Aug-19";
int i = strdiff(s1,s2);
printf("%d %c\n",i, s1[i]);
return 0;
}
Mind you, comparing two files line by line may turn out to be a bigger mission than it sounds if the two files you are comparing do not have exactly the same lines (with minor differences of course).
I need to read a text file with 7 lines into 7 different variables. The text file looks like this:
.2661
A.txt
B.txt
C.txt
1
2
0.5 0.6
These are the variables that I need to store each line into:
float value1; // line 1 from .txt file
char *AFileName; // line 2 from .txt file
char *BFileName; // line 3 from .txt file
char *CFileName; // line 4 from .txt file
int value2; // line 5 from .txt file
int lastLineLength; // line 6 from .txt file
double lastLine[lastLineLength]; // line 7 from .txt file - this can be different lengths
I have currently been doing this by just using the arguments when I call my program from the command line and the argv command.
First open the file using fopen with read access:
FILE *inputFile = fopen(filename, "r");
if(!inputFile) {
// Error opening file, handle it appropriately.
}
Then read the data from the file using fscanf. The first parameter is the FILE * we created above. The second parameter is a format string that specifies what fscanf should expect while reading the file. The remaining parameters are pointers to variables that will hold the data read from the file.
int variablesFound;
variablesFound = fscanf(inputFile, "%f\n%s\n%s\n%s\n%d\n%d\n", &value1, AFileName, BFileName, CFileName, &value2, &lastLineLength);
if(variablesFound < 6) {
// There was an error matching the file contents with the expected pattern, handle appropriately.
}
double lastLine[lastLineLength];
// Iterate over the last line.
int lastLineIndex;
for(lastLineIndex = 0; lastLineIndex < lastLineLength; lastLineIndex++) {
fscanf(inputFile, "%lf", &lastLine[lastLineIndex]);
fscanf(inputFile, " "); // Eat the space after the double.
}
Edit
After comments I realized it might be worth noting that you have to allocate memory to your variables as the real first step. The primitives (those with an & below) can be declared as normal. For the string (char array), you'll want to do one of the following:
char *aFileName = calloc(MAX_FILENAME_SIZE + 1, sizeof(char));
or
char aFileName[MAX_FILENAME_SIZE + 1];
Depending on what your purpose with aFileName would be determines which method would be appropriate. However, assuming this code appears in the main or doesn't need to exist beyond the scope of the function, the latter would be better as it doesn't require free()ing the variable after you're done with it.
It also may be worth while singling out the code that deals with reading input if your requirements change often.
You can read from the file as follows:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE * fp;
char * line = NULL;
size_t len = 80;
fp = fopen("<path to your file>", "r");
if (fp == NULL)
exit(-1);
while (getline(&line, &len, fp) != -1)
printf("%s", line);
fclose(fp);
return 0;
}
getline reads character strings from the file, so you'd have to parse the lines as needed (atoi, atof).
I am trying to read lines from a list to my structs, and it is almost working. I am not really sure what the problem is, but the last line of the text file wont show up when I call for the structs and I do not think the words are placed right...
void loadFile(char fileName[], Song *arr, int nrOf) {
FILE *input = fopen(fileName, "r");
if (input == NULL) {
printf("Error, the file could not load!");
} else {
fscanf(input, "%d", &nrOf);
fscanf(input, "%*[^\n]\n", NULL);
for (int i = 0; i < nrOf; i++) {
fgets(arr[i].song, sizeof(arr[i].song), input);
fgets(arr[i].artist, sizeof(arr[i].artist), input);
fgets(arr[i].year, sizeof(arr[i].year), input);
}
for (int i = 0; i < nrOf; i++) {
printf("%s", arr[i].song);
printf("%s", arr[i].artist);
printf("%s", arr[i].year);
}
rewind(input);
printf("The file is now ready.\n");
}
fclose(input);
}
The text file starts with a number on the first line to keep track of how many songs there are in the list. I therefore tried with this:
fscanf(input, "%d", &nrOf);
fscanf(input, "%*[^\n]\n", NULL);
to be able to skip the first line after nrOf got the number.
EDIT:
Here is the struct:
typedef struct Song {
char song[20];
char artist[20];
char year[5];
} Song;
Here is the text file:
4
Mr Tambourine Man
Bob Dylan
1965
Dead Ringer for Love
Meat Loaf
1981
Euphoria
Loreen
2012
Love Me Now
John Legend
2016
And the struct is dynamic allocated:
Song *arr;
arr = malloc(sizeof(Song));
there are a combination of reasons why the last line(s) do not print
The main reason is the last line(s) were never read
Should not call fclose() in any execution path where the file failed to open
there is no need to call rewind() when the next statement is fclose()
Since the calls to printf() for the fields in the Song array are output, one right after another, this will result in a long long single line output to the terminal, Hopefully the terminal is set to automatically scroll after so many columns of output, but that cannot be depended upon.
When outputting an error message, it is best to output it to stderr, not stdout. The function: perror() does that AND also outputs the reason the OS thinks the error occurred. (it does this by referencing errno to select which error message to output.)
the following is the key problem:
if the input file contains one song info per line then the field year will either contain a trailing newline or the newline will not have been read. If the newline was not read, then the next call to fgets() which was trying to input the song title will only receive a newline then all following fields (of all songs) will be progressively further off.
Suggest after reading a song fields, use a loop to clear out any remaining characters in the input line, similar to:
int ch;
while( (ch = getchar( input )) && EOF != ch && '\n' != ch );
I have 5 list of name
char *name[] = {"a","b","c","d","e"};
and I have 3 files
char path1[PATH_MAX+1]
snprintf(path1, PATH_MAX+1, "%sfile1.txt",dirname);
FILES *filename1 = fopen(path1, "w")
.
.
.
char path3[PATH_MAX+1]
snprintf(path3, PATH_MAX+1, "%sfile3.txt",dirname);
FILES *filename3 = fopen(path3, "w")
What I want is to randomly append a,b,c,d,e (one of them per file) into three of those files without repetition.
What I have right now is (example from one of them)
srand(time(NULL));
int one = rand()%5;
char path1[PATH_MAX+1];
snprintf(path1, PATH_MAX+1, "%sfile1.txt",dirname);
FILES *filename1 = fopen(path1, "w");
fputs(name[one],filename1);
fclose(filename1);
However, sometimes it is still possible where my file1.txt and file3.txt both contain b (same alphabet from name)
Questions
Did I miss something to make sure that all the random result always unique?
Is it also efficient tho to have 6 lines of code to create one file and append a random name inside it? I'm just wondering if I have to create like 20 files, I will write 120 lines that basically almost the same, just different in number (filename1 to filename3)
Thank you.
To get a unique sequence of characters, you can draw them from a diminishing pool. For example, after you have picked the a, it is removed from the pool. Of course, the pool must be at least as big as the number of files you want to print.
A simple way to implement this sort of picking is to pick a char from the pool, move the last character from the pool to the place of the picked character and decrease the pool size by one.
If you see a lot of repetition of code, especially if the only difference is a variable name along the lines of filename1, filename2, filename3 and so on should ring a bell that you should use an array: FILE *file[NFILE]. Be aware, though, that you can only have a certain number of files open at a time.
In your case, you want to write a single character to a file. There's no need to have multiple file s open simultaneously: Open a file, pick a char, write it to the file, close e file. Then process the next file.
The program below does what you want, I think.
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#define NFILES 10
int main()
{
char pool[] = "abcdefghij"; // Available chars as array
int npool = sizeof(pool) - 1; // Pool size minus terminating '\0'
int i;
if (npool < NFILES) {
fprintf(stderr,
"Not enough letters in pool for %d files.\n", NFILES);
exit(1);
}
srand(time(NULL));
for (i = 0; i < NFILES; i++) {
int ipick = rand() % npool; // position of pick
char pick = pool[ipick]; // picked char
char fname[20];
FILE *f;
pool[ipick] = pool[--npool]; // remove pick from pool
snprintf(fname, sizeof(fname), "file-%03d.txt", i);
f = fopen(fname, "w");
if (f == NULL) {
fprintf(stderr, "Could not create %s.\n", fname);
exit(1);
}
fprintf(f, "%c\n", pick);
fclose(f);
}
return 0;
}
I'm creating a program in C, which receive a file and reverses each line like this:
$ cat file
Line 1
Line 2!
Line_3
$ ./reverse < file
1 eniL
!2 eniL
3_eniL
But I get an error and I don't know why.
In the program, I made a loop which:
Read a line from stdin to a string of 2048 chars.
Get the strlen from the read line and made a new string.
Copy, char by char, the characters of the string to the new string, in reverse order.
But, running the program, sometimes copy a char and other simbols (#) getting something like this:
http://i.stack.imgur.com/G8VTx.png
Some strings get # simbols depending on the length. Here, another example:
http://i.stack.imgur.com/1UKdL.png
The files are in us-ansii.
The code of the program is:
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
#include <stdbool.h>
int main(int argc, char *argv[]){
char string[2048];
bool final = false;
while(!final){
fgets(string,2048,stdin); // Read line
if(feof(stdin))
final=true;
else{
int length;
length = (string[strlen(string)-1] == '\n') ? strlen(string)-1 : strlen(string);
char reverseStr[length];
// Loop
int count = length;
for(int i=0;i<length;i++){
reverseStr[i]=string[count-1];
count--;
}
printf("%s\n",reverseStr);
}
}
}
I have tried changing the loop:
for(int i=0;i<length;i++){
reverseStr[count-1]=string[i];
count--;
}
Whit pointers:
for(int i=0;i<length;i++){
char * pr = $reverseStr[count-1];
*pr=string[i];
count--;
}
# Symbol use to appear in the same position.
"Febrero" gets "orerbeF"
"Febrerol" gets "lorerbeF#"
"Febreroll" gets "llorerbe#" (lose the "F")
Here an image of the gdb:
When program is reversing second line "Febreroll"
reverseStr is "llorer" (in $12)
In the next iteration:
reverseStr is "llorerb" (in $14)
In the next iteration, program copy an "e" and more things:
reverseStr is "llorerbe\221\b#" (in $16)
GDB IMAGE IN A COMENT↓↓↓
This occurs because the code does not take care to insure there is a NUL character at the termination. The # happens to appear in the (uninitialized) buffer at the end, then presumably a random NUL is after that.
To fix this, do something like:
for(int i=0;i<length;i++){
reverseStr[i]=string[count-1];
count--;
}
reverseStr[length] = '\000'; // I added this.
You have the length stored as a separate count rather than a NUL terminator. So use the length in the format string.
printf("%*s\n", length, reverseStr);