I am setting a pointer to pointer array of variables that will open the files and count the words. The issue I am having is how to print the file name in the output. The counting of the words work (minus a few small accounting for double spaces and such).
The files are entered as command line arguments. Here is the code:
FILE **Files = malloc(sizeof(FILE *) * (count)); //Array of file pointers
for (i = 0 ; i < count ; i++)
{
Files[i] = fopen(argv[i + 1], "r");
}
for (i = 0 ; i < count ; i++)
{
wordCount = countWords(Files[i]);
printf("File %s: number of words is: %d\n", Files[i], wordCount);
}
I have left out the error checking on if the fopen == NULL to keep it short. I have tried *Files[i], *(&Files[i]) and a few others in an attempt to print out the contents of the variable. Is there a way to print out a FILE * varible as a string?
Since argv[i + 1] is the filename, you need to print argv[i + 1]. You don't want to print Files[i] since it is not a pointer to the filename or anything related in any way to the filename.
You may not be aware of this, but a filename does not uniquely identify a file. A file can have no name at all (for example, if it is removed from the only directory it is in while it's open) or can have more than one name (for example, if it's hard linked into multiple directories). Trying to go from the file to the filename is not simple and something you should only do if you have some specific reason.
Related
I have this code. This is the file content
nasa.txt
news1.txt
file11.txt
mixed.txt
planets.txt
file21.txt
info31.txt
This is the code
FILE *fp = fopen(collectionFilename, "r");
char url[MAX_WORD + 1];
char *urls[MAX_WORD + 1];
while(fscanf(fp, "%s", url) == 1){
urls[index++] = url;
}
for(int i = 0; i < index; i++){
printf("%s\n", urls[i]);
}
fclose(fp);
I want the code to read in the file names and store them into my urls array. However my printf statement at the end prints info31.txt 7 times and I'm a bit confused as to why this happens.
Thank you!
From your code there are two steps in the loop
fscanf(fp, "%s", url) will read string and write the string to url
urls[index++] = url; will assign urls[index] to point to url and increase index by one
The key point is that in step 2, you just repeat the same thing to every elements in urls -- pointing them to url, which is why you get the same URL from urls. You never copy the characters from url to somewhere else and fscanf just keep using the same buffer for input in every iteration.
I'm try to get my text to be read back to front and to be printed in the reverse order in that file, but my for loop doesn't seem to working. Also my while loop is counting 999 characters even though it should be 800 and something (can't remember exactly), I think it might be because there is an empty line between the two paragraphs but then again there are no characters there.
Here is my code for the two loops -:
/*Reversing the file*/
char please;
char work[800];
int r, count, characters3;
characters3 = 0;
count = 0;
r = 0;
fgets(work, 800, outputfile);
while (work[count] != NULL)
{
characters3++;
count++;
}
printf("The number of characters to be copied is-: %d", characters3);
for (characters3; characters3 >= 0; characters3--)
{
please = work[characters3];
work[r] = please;
r++;
}
fprintf(outputfile, "%s", work);
/*Closing all the file streams*/
fclose(firstfile);
fclose(secondfile);
fclose(outputfile);
/*Message to direct the user to where the files are*/
printf("\n Merged the first and second files into the output file
and reversed it! \n Check the outputfile text inside the Debug folder!");
There are a couple of huge conceptual flaws in your code.
The very first one is that you state that it "doesn't seem to [be] working" without saying why you think so. Just running your code reveals what the problem is: you do not get any output at all.
Here is why. You reverse your string, and so the terminating zero comes at the start of the new string. You then print that string – and it ends immediately at the first character.
Fix this by decreasing the start of the loop in characters3.
Next, why not print a few intermediate results? That way you can see what's happening.
string: [This is a test.
]
The number of characters to be copied is-: 15
result: [
.tset aa test.
]
Hey look, there seems to be a problem with the carriage return (it ends up at the start of the line), which is exactly what should happen – after all, it is part of the string – but more likely not what you intend to do.
Apart from that, you can clearly see that the reversing itself is not correct!
The problem now is that you are reading and writing from the same string:
please = work[characters3];
work[r] = please;
You write the character at the end into position #0, decrease the end and increase the start, and repeat until done. So, the second half of reading/writing starts copying the end characters back from the start into the end half again!
Two possible fixes: 1. read from one string and write to a new one, or 2. adjust the loop so it stops copying after 'half' is done (since you are doing two swaps per iteration, you only need to loop half the number of characters).
You also need to think more about what swapping means. As it is, your code overwrites a character in the string. To correctly swap two characters, you need to save one first in a temporary variable.
void reverse (FILE *f)
{
char please, why;
char work[800];
int r, count, characters3;
characters3 = 0;
count = 0;
r = 0;
fgets(work, 800, f);
printf ("string: [%s]\n", work);
while (work[count] != 0)
{
characters3++;
count++;
}
characters3--; /* do not count last zero */
characters3--; /* do not count the return */
printf("The number of characters to be copied is-: %d\n", characters3);
for (characters3; characters3 >= (count>>1); characters3--)
{
please = work[characters3];
why = work[r];
work[r] = please;
work[characters3] = why;
r++;
}
printf ("result: [%s]\n", work);
}
As a final note: you do not need to 'manually' count the number of characters, there is a function for that. All that's needed instead of the count loop is this;
characters3 = strlen(work);
Here's a complete and heavily commented function that will take in a filename to an existing file, open it, then reverse the file character-by-character. Several improvements/extensions could include:
Add an argument to adjust the maximum buffer size allowed.
Dynamically increase the buffer size as the input file exceeds the original memory.
Add a strategy for recovering the original contents if something goes wrong when writing the reversed characters back to the file.
// naming convention of l_ for local variable and p_ for pointers
// Returns 1 on success and 0 on failure
int reverse_file(char *filename) {
FILE *p_file = NULL;
// r+ enables read & write, preserves contents, starts pointer p_file at beginning of file, and will not create a
// new file if one doesn't exist. Consider a nested fopen(filename, "w+") if creation of a new file is desired.
p_file = fopen(filename, "r+");
// Exit with failure value if file was not opened successfully
if(p_file == NULL) {
perror("reverse_file() failed to open file.");
fclose(p_file);
return 0;
}
// Assumes entire file contents can be held in volatile memory using a buffer of size l_buffer_size * sizeof(char)
uint32_t l_buffer_size = 1024;
char l_buffer[l_buffer_size]; // buffer type is char to match fgetc() return type of int
// Cursor for moving within the l_buffer
int64_t l_buffer_cursor = 0;
// Temporary storage for current char from file
// fgetc() returns the character read as an unsigned char cast to an int or EOF on end of file or error.
int l_temp;
for (l_buffer_cursor = 0; (l_temp = fgetc(p_file)) != EOF; ++l_buffer_cursor) {
// Store the current char into our buffer in the original order from the file
l_buffer[l_buffer_cursor] = (char)l_temp; // explicitly typecast l_temp back down to signed char
// Verify our assumption that the file can completely fit in volatile memory <= l_buffer_size * sizeof(char)
// is still valid. Return an error otherwise.
if (l_buffer_cursor >= l_buffer_size) {
fprintf(stderr, "reverse_file() in memory buffer size of %u char exceeded. %s is too large.\n",
l_buffer_size, filename);
fclose(p_file);
return 0;
}
}
// At the conclusion of the for loop, l_buffer contains a copy of the file in memory and l_buffer_cursor points
// to the index 1 past the final char read in from the file. Thus, ensure the final char in the file is a
// terminating symbol and decrement l_buffer_cursor by 1 before proceeding.
fputc('\0', p_file);
--l_buffer_cursor;
// To reverse the file contents, reset the p_file cursor to the beginning of the file then write data to the file by
// reading from l_buffer in reverse order by decrementing l_buffer_cursor.
// NOTE: A less verbose/safe alternative to fseek is: rewind(p_file);
if ( fseek(p_file, 0, SEEK_SET) != 0 ) {
return 0;
}
for (l_temp = 0; l_buffer_cursor >= 0; --l_buffer_cursor) {
l_temp = fputc(l_buffer[l_buffer_cursor], p_file); // write buffered char to the file, advance f_open pointer
if (l_temp == EOF) {
fprintf(stderr, "reverse_file() failed to write %c at index %lu back to the file %s.\n",
l_buffer[l_buffer_cursor], l_buffer_cursor, filename);
}
}
fclose(p_file);
return 1;
}
Alright guys, I'm having some trouble with using my file pointers to traverse through a file via looping. I will have a list of strings in my text file, one per line, and I am testing similarities between them. So my method of going about it is having two file pointers to traverse and compare.
Example: FILE* fp1 will be set on the first line to begin. FILE* fp2 will be set on the second line to begin.
I wish to traverse this way:
Line 1 <-> Line 2
Line 1 <-> Line 3
Line 1 <-> Line 4
Line 1 <-> Line 5
(Here I read the next line via fp1 to get to Line 2, I also attempt to set fp2 to the next line read after fp1)
Line 2 <-> Line 3
Line 2 <-> Line 4
Line 2 <-> Line 5
Etc...
And here is the code... The FILE* fp was passed to the function as (FILE* fp)
FILE* nextfp;
for(i = 1; i <= numStr; i++){
fscanf(fp, "%s", str1);
nextfp = fp;
double str1len = (double)(strlen(str1));
for(j = i + 1; j <= numStr; j++){
fscanf(nextfp, "%s", str2);
double str2len = (double)(strlen(str2));
if((str1len >= str2len) && ((str2len / str1len) >= 0.90000) && (lcsLen(str1, str2) / (double)str2len >= 0.80000))
sim[i][j] = 'H';
else if ((str2len >= str1len) && ((str1len / str2len) >= 0.90000) && (lcsLen(str2, str1) / (double)str1len >= 0.80000))
sim[i][j] = 'H';
}
}
int numStr is the total number of lines with strings
lcsLen(char*, char*) returns length of longest common subsequence
The sim[][] array is where I am labeling my level of similarity. As of right now I only have it programmed to label strings of high similarity.
My results are incomplete and it is due to my fp not going to the next line and just staying on the same string, AND, my inner loop is keeping the nextfp pointing at the last string and not going where it should due to my nextfp = fp line.
Any help's appreciated! Thank you all so much!
You can't treat FILE * like a pointer to memory, it's a pointer to an object of type FILE which in turn holds the state associated with the file I/O.
Copying a FILE * makes little sense, and certainly doesn't create a copy of the state in question.
Part of that state is the current position in the file, this doesn't change just because you copy the pointer.
You should either investigate memory-mapping the file, which would give you the type of access you seem to expect, or just read in the entire file once to an array of strings, which you can then iterate over in any way you like.
After first innerloop, the file stream already goes to end-of-file. After that you can't use fp to read from the file stream. Remember you are reading on stream, stream don't go back. Read man 3 fseek, you can manualy set the file offset to some place, but this doesn't address your problem . You should read all lines to arrays, this is easier and faster.
As the other answers state, you should consider just reading the whole file into an array. If your file size is more than several hundreds of MB, your approach might be the right choice however.
Use ftell to save the current offset after reading the first line and set the file descriptor back to that offset with fseek after you looped through the rest of the lines.
FILE* nextfp;
size_t offset;
for(i = 1; i <= numStr; i++){
fscanf(fp, "%s", str1);
offset = ftell(fp); // save the current position
double str1len = (double)(strlen(str1));
for(j = i + 1; j <= numStr; j++){
fscanf(nextfp, "%s", str2);
double str2len = (double)(strlen(str2));
if((str1len >= str2len) && ((str2len / str1len) >= 0.90000) && (lcsLen(str1, str2) / (double)str2len >= 0.80000))
sim[i][j] = 'H';
else if ((str2len >= str1len) && ((str1len / str2len) >= 0.90000) && (lcsLen(str2, str1) / (double)str1len >= 0.80000))
sim[i][j] = 'H';
}
fseek(fp, offset, SEEK_SET); // set the file descriptor back to the previous position
}
I have been looking around for a solution but cannot seem to find a solution to my question so I will ask it. I am working in C and am reading in a .txt and taking all the values and storing them in an array then doing various tasks with them. Now my problem is that no matter what I do I cannot get file pointer I create to point to the file for some reason. I have done this for projects in the past and have compared my code then to the current one and cannot see the issue. The filename needs to be read in from the command line as well. I think there is something wrong with what I'm passing through the command line but am not sure. I have stepped through and the filename is being passed correctly but when it tries to open I get a null pointer so there is just something I'm missing.
The text file will contain a series of numbers, the first number will be the number of numbers in the file after that first number. (So if the number is 10 then there will be ten numbers after 10 is read in) after that first number the remaining numbers will be 0-9 in a random order.
Below is my current chunk of code only involving reading of the file and storing its data. (I already know the array will be of size 10 which is why the array is declared with that size.)
int main(int argc, char *argv[])
{
char* filename = "numbers.txt";
int arr[10];
int numElem;
int indexDesired = 0;
FILE *fp;
fp = fopen(filename, "r"); // open file begin reading
if (!fp)
{
printf("The required file parameter name is missing\n");
system("pause");
exit(EXIT_FAILURE);
}
else
{
fscanf(fp, "%d", &numElem); //scans for the first value which will tell the number of values to be stored in the array
int i = 0;
int num;
while (i <= numElem) //scans through and gets the all the values and stores them in the array.
{
fscanf(fp, "%d", &num);
arr[i] = num;
i++;
}
fclose(fp);
}
}
***note: My sort and swap method work perfectly so I have omitted them from the code as the error happens before they are even called.
you said,
The filename needs to be read in from the command line as well.
However, you are using:
char* filename = "numbers.txt";
and
fp = fopen(filename, "r"); // open file begin reading
No matter what you are passing in the command line, the file you are trying to open is "numbers.txt".
Things to try:
Use the full path name of "numbers.txt" instead of just the name of the file.
char* filename = "C:\\My\\Full\\Path\\numbers.txt";
If that doesn't work, you will probably have to deal with permissions issues.
Pass the file name from the command line, using the full path. That should work if there are no permissions issues.
if ( argc < 2 )
{
// Deal with unspecified file name.
}
char* filename = argv[1];
Pass the relative path of the file name. If you are testing your program from Visual Studio, you have to make sure that you use the path relative to the directory from where Visual Studio launches your program.
while (i <= numElem)
should be
while (i < numElem)
Because in fscanf(fp, "%d", &numElem); you are scanning the number of elements.
Notice that the array in C starts from 0, so if say numElem is 10 arr[10] does not exist which can be harmful because arr goes from arr[0] to arr[9]
Also, you should check if numElem is lower than 10 before the while(i < numElem) loop.
I'm trying to do a substring search using sys call where I open a file from the command line, and compare the following command line arguments to the file. I want to output the number of occurrences of each substring. For example, if I wrote ./a.out filename aa b I am looking for the number of times aa and b occurs in filename.
My code so far
for(int num = 4; num < argc; num++)
{
int fp = open (argv[1], O_RDONLY);
int sizeofbar = strlen(argv[1]);
char *buf = (char*)malloc(sizeofbar+1);
int count = 0; //counter for output
char* string2 = argv[num];
int sizeofcompare = strlen(string2);
read(fp, buf, sizeofcompare);
while (strstr(buf, string2) != NULL)
{
count++;
buf++;
}
I think you need to do some initialization before entering your loop. Perhaps you want to take the first argument off first:
filename = argv[0];
argv++;
argc--;
int fp = open(...
Next, I'd pre-process the rest of the arguments to build a data structure to store it. You can use the argc value to determine how many words you'll need to track.
counts = (int *)calloc(argc, sizeof(int));
Note that this will initialize the values to zero for you too.
With everything setup, then I'd read through the entire file contents and compare to the strings. The trick is in comparing multiple different length strings at once. A simple yet inefficient method is to read the entire file contents in and then loop using strstr for each word that follows the filename. Another method would be mapping the file's contents into memory and scanning it directly (letting the operating system do the heavy lifting).