I'm a newbie at the C programming language and am having a lot of trouble writing this specific program. This program needs to take user input for a word, which the program will then check in the current directory for all .txt files, search for that word in those files, and replace the word in those files but as an uppercase word. For example, if the user inputs "apple" as an input, then the program would read all .txt files in the current directory (including subdirectories) and replace every single substring as an uppercase of apple (apple --> APPLE; applesauce --> APPLEsauce).
Here is my current code:
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define BUFFER_SIZE 1000
int main(int argc, char **argv)
{
printf("Enter the target string: %s\n", argv[1]);
DIR *d;
struct dirent *dir;
char buffer[BUFFER_SIZE];
char buf[100];
d = opendir(".");
printf("Searching in current directory\n");
if (d)
{
while ((dir = readdir(d)) != NULL)
{
int length = strlen(dir->d_name);
if (strncmp(dir->d_name + length - 4, ".txt", 4) == 0)
{
FILE *fp = fopen(dir->d_name, "r");
FILE *ftemp = fopen("replace.tmp", "w");
while (fscanf(fp, "%s", buf) != EOF)
{
if (strcmp(argv[1], buf) == 0)
{
printf("%s\n", dir->d_name);
printf(("%s\n", &buf));
for (unsigned int i = 0, n = strlen(argv[1]); i < n; i++)
{
argv[1][i] = (char)toupper(argv[1][i]);
}
printf(("%s\n", argv[1]));
}
}
}
}
closedir(d);
}
return 0;
}
I've been able to have the original word stored in buf for now, and the uppercase word stores in argv[1].
My problem is that I've been stuck for hours on replacing the string in the .txt file with the updated uppercase word. If anybody could provide some guidance, this would be greatly appreciated.
foreach directory entry, test if d_type is
DT_DIR: directory or
DT_REG: file
in case of directory, open the directory and start from 1.
in case of file and the extension is ".txt"
open file in "r+" (read/write) mode
read a complete line via fgets
for each occurrence of search string in line
get the current file position via ftell -> file_pos
subtract from the file position the length of the line -> line_pos
add to the position of the line the position of the word -> word_pos
set new file position via fseek(file, word_pos, SEEK_SET)
write the uppercase word into file
reset the file position fseek(file, file_pos, SEEK_SET)
goto 2.
Related
I am trying to traverse a filesystem tree. When I come across a file with a certain extension I want to open the file and then count the lines in the file. It seems I am getting a segmentation fault I believe it is after/when I open the file and try to count the lines. Any help on why this is seg faulting would be appreciated.
EDIT:
I have deleted the old code because I fixed the seg fault. Now I changed the data to be fed in at the command line. But it seems either the files are not getting opened or it is not counting the lines correctly because when I run it the program will always return 0 lines.
Here is the updated code:
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
const char *get_filename_ext(const char *filename) {
const char *dot = strrchr(filename, '.');
if(!dot || dot == filename) return "";
return dot + 1;
}
int printdir(char *dir, char *targetFileExt, int depth)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
int spaces = depth*4;
int totalLines=0;
if((dp = opendir(dir)) == NULL) {
fprintf(stderr,"cannot open directory: %s\n", dir);
return -1;
}
chdir(dir);
while((entry = readdir(dp)) != NULL) {
lstat(entry->d_name,&statbuf);
if(S_ISDIR(statbuf.st_mode)) {
/* Found a directory, but ignore . and .. */
if(strcmp(".",entry->d_name) == 0 || strcmp("..",entry->d_name) == 0){
continue;
}
printf("%*s%s/\n",spaces,"",entry->d_name);
/* Recurse at a new indent level */
totalLines = printdir(entry->d_name, targetFileExt, depth+1);
}
else {
printf("%*s%s\n",spaces,"",entry->d_name);
char *currentFileExt = get_filename_ext(entry->d_name);
if(*currentFileExt == *targetFileExt){
//open the file for reading
FILE *fPtr = fopen(entry->d_name, "r");
//traverse the file
while(!feof(fPtr)){
//if there is a new line character
int temp = fgetc(fPtr);
if(temp=='\n'){
//add a line to the total amount of lines
totalLines++;
}
}
//close the file
fclose(fPtr);
fPtr=NULL;
}
}
}
chdir("..");
closedir(dp);
return totalLines;
}
int main(int argc, char* argv[])
{
char *topdir, pwd[2]=".";
char *ext;
if (argc < 2 || argc > 3)
topdir=pwd;
else if(argc == 2){
topdir=argv[1];
}
else if(argc == 3){
topdir=argv[1];
ext=argv[2];
}
printf("Directory scan of %s\n",topdir);
int lines = printdir(topdir, ext, 0);
printf("You have written %d lines of %s code!\n", lines, ext);
return 0;
}
First of all, the filename extension check: if(*currentFileExt == *targetFileExt) Will only work for file extensions with a single character. Consider searching for ".com", and you encounter a ".c" file. get_filename_ext() will return a pointer to the first character after the dot. Then you would be comparing 'c' == 'c'. Consider using strcmp() instead, and make sure targetFileExt does not contain the leading dot, as this is how your code is set up as-is.
Second of all, printdir() in its current form does not accumulate the line count from the subdirectories.
Consider the scenario:
We're searching for .c files.
The directory you're searching in contains two subdirectories, A and B, and nothing else.
A contains a 10 LOC .c file, and B contains a 20 LOC .c file.
When you run the code:
You call printdir() from main(), let's say your code first encounters A
The function calls itself recursively and returns 10, so totalLines gets assigned a value of 10.
On the next loop iteration the function encounters B.
The function calls itself recursively, returns 20, so totalLines gets assigned a value of 20.
You have lost the 10 lines from the first loop iteration.
In order to fix this, you have three options:
Change the function signature to: int printdir(char *dir, char *targetFileExt, int depth, int totalLines); and remove int totalLines=0;. In the function call it like: totalLines = printdir(entry->d_name, targetFileExt, depth+1, totalLines); Call it from main() passing 0 for totalLines.
Change to function signature to accept a pointer to a line count variable, and increment it when you encounter lines. (impl. left as homework)
Use a global line count variable. (impl. left as homework)
I am trying to create a c program that reads the extensions of the files in its current working directory.
The program then creates folders that correspond to the extensions of the files. It then copies each file
from its cwd into the corresponding folder.
e.g:
hello.txt into created .txt folder
The code successfully creates folders for all the file extensions in the current directory, but crashes when it begins to copy.
Here is the whole code.
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <dir.h>
#include <stdlib.h>
#include <errno.h>
int main(void) {
FILE *input, *output; // Two files, input and output
char ch; // ch is used to assign characters from input file which will then be copied into the output file
char *exe = ".exe";
struct dirent *de;
DIR *dr = opendir("."); // Open directory for reading
printf("%s", dr->dd_name);
// If directory doesn't exist, quit
if(dr == NULL) {
printf("Can't open current directory.");
return 0;
}
// Loop first to create a directory corresponding to all
// extensions present in the current directory
while((de = readdir(dr)) != NULL) {
char *filename = de->d_name; // Get the filename
char *ext = strrchr(filename, '.'); // Get the extension
if(!(!ext || ext == filename)){ // Compare extension
int check = mkdir(ext);
if(!check)//Check if the directory was created
printf("Directory created successfully.\n");
else {
printf("Unable to create directory.\n");
}
}
}
// Close the directory so as to reset the pointer ready for the next read.
closedir(dr);
dr = opendir(".");
// Loop reading each file and checking which
// directory it corresponds to.
while((de = readdir(dr)) != NULL) {
char *filename = de->d_name; // Get the filename
char *ext = strrchr(filename, '.'); // Get the extension
if(!(!ext || ext == filename)){ // Check for a valid extension
DIR *file_ext_dir = opendir(ext); // Open the dir that corresponds to the extension of the file.
char *dir_name = file_ext_dir->dd_name; //Get the dir name of the opened directory
if(file_ext_dir && (strcmp(dir_name, "..") != 0)&& (strcmp(ext, exe) !=0) ) { //ignore .exe and the cwd
printf("Successfully opened: %s dir\n", file_ext_dir->dd_name);
char *output_path = strcat(dir_name, filename);
printf("Ready to copy files from %s to: %s\n", filename, output_path);
output = fopen(output_path, "a+"); // Open output.txt for appending, if doesn't exist, create it.
input = fopen(filename, "r"); // Open the input file ()'filename') for reading
while(1) { // Loop through the input file
ch = fgetc(input); // Get the current character
if(ch == EOF) break; // Stop if EOF is found
putc(ch, output); // Put current character from the input file into output.txt
}
fclose(input); // Close input file
fclose(output); // Close output file
closedir(file_ext_dir);
} else if(ENOENT == errno){ //Check if there is no such directory and handle the error
printf("Dir does not exist.");
}else {
continue; //Skip that file if for some reason the directory cannot be opened.
}
}
}
closedir(dr); // Close directory
printf("Created directories and copied all files to that correspond to them.");
return 0;
}
My main objective here is to make use of fscanf() to take in each word from my file and store it into an array location. As it stands, I loop through the file setting each word to a location in wordList[]. I can print out the values as they are put into the array and each seems to be placed correctly. But after the loop, when I attempt to print only one of the values (simply checking that everything went as it should) I get a weird output. When printing the string contained in wordList[5], it prints the first character of every word after location [5], and prints the last word that was collected.
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void readFile (FILE *fPtr, char *fileName) {
FILE *newFilePtr;
char wordList[1000];
int i = 0;
newFilePtr = fopen(strcat(fileName, ".out"), "w"); // Blank document created under same file name, but with ".out"
while(fscanf(fPtr, "%s", &wordList[i]) == 1) { // Read in strings from main file into wordList
printf("%s\n", &wordList[i]);
++i;
if (i > 10) // KEEP OUTPUT SHORT FOR STACK OVERFLOW QUESTION
break;
}
printf("%s\n", &wordList[5]); // PRINTS WILD VALUE AT POSITION 5
fclose(newFilePtr);
}
int main (int argc, char *argv[]) {
int lineSize;
char *fileName = argv[2]; // Store name of file for future operations
FILE *fPtr;
if (argc != 3) {
fprintf(stderr, "%s", "ERROR: Incorrect arguments. Please input a line size and a file.\n");
return;
}
lineSize = atol(argv[1]); // Convert string to it's integer equivalent
if (lineSize < 25 || lineSize > 100) {
fprintf(stderr, "%s", "ERROR: Line size not within range.\n");
return;
}
if (fPtr = fopen(fileName, "r")) { // If the file exists, open it for reading
readFile(fPtr, fileName);
puts("FILE OPENED SUCCESS");
fclose(fPtr);
} else {
fprintf(stderr, "%s", "ERROR: File could not be opened.\n");
return;
}
return;
}
And my current output (constrained to just the first 10 values to keep it short):
Mason–Dixon
Line
(or
Mason
and
Dixon's
Line)
was
surveyed
between
1763
DLwsb1763 // Should print "Dixon's" (the string at location 5)
FILE OPENED SUCCESS
The creation of the new file at line 11 is for later use when the file is formatted. For now, i'm only concerned with properly scanning in the values from the original file.
I have created a function that takes as a parameter the name of a source file, the name of a destination file and the beginning and end lines of the source file lines that will be copied to the destination file, like the example below. All I want to do is to input the lines that I want to copy to the other text file like the example below:
The code I show you just "reads" the content of the one text file and "writes" another one. I want to "write" specific lines that the user gives, not the whole text file
Inputs by the user:
Source_file.txt //the file that the destination file will read from
destination_file.txt //the new file that the program has written
2 3 // the lines that it will print to the destination file: 2-3
Source_file.txt:
1
2
3
4
5
6
destination_file.txt
2
3
code:
#include <stdio.h>
#include <stdlib.h>
void cp(char source_file[], char destination_file[], int lines_copy) {
char ch;
FILE *source, *destination;
source = fopen(source_file, "r");
if (source == NULL) {
printf("File name not found, make sure the source file exists and is ending at .txt\n");
exit(EXIT_FAILURE);
}
destination = fopen(destination_file, "w");
if (destination == NULL) {
fclose(source);
printf("Press any key to exit...\n");
exit(EXIT_FAILURE);
}
while ((ch = fgetc(source)) != EOF)
fputc(ch, destination);
printf("Copied lines %d from %s to %s \n",
lines_copy, source_file, destination_file, ".txt");
fclose(source);
fclose(destination);
}
int main() {
char s[20];
char d[20];
int lines;
printf("-Enter the name of the source file ending in .txt\n"
"-Enter the name of the destination file ending in .txt\n"
"-Enter the number of lines you want to copy\n\n");
printf(">subcopy.o ");
gets(s);
printf("destination file-> ");
gets(d);
printf("Lines: ");
scanf("%d", &lines);
cp(s, d, lines);
return 0;
}
In cp(), in order to select the lines to keep, you have to know their position in the input-file. Thus, you need to count lines.
Using fgets instead of fgetc will allow you to count the lines.
On the other hand, if I wanted to select lines 3 and 7 to 12 in a file, I'd use:
sed -n -e "3p;7,12p" < input.txt > output.txt
this is a very simple solution, let's say you know that the maximun length of a line will be 100 characters for simplicity (if a line is longer than 100 characters only the first 100 will be taken)
at the top (outside main) you can write
#ifndef MAX_LINE_SIZE
#define MAX_LINE_SIZE 100
#endif
i know many people don't like this but i think in this case it makes the code more elegant and easier to change if you need to modify the maximum line size.
to print only the wanted lines you can do something like this
char line[MAX_LINE_SIZE];
int count = 0;
while (fgets(line, MAX_LINE_SIZE, source)){
count++;
if (3 <= count && count <= 5){
fputs(line, destination);
}
}
The while loop will end when EOF is reched because fgets returns NULL.
P.S. there could be some slight errors here and there since i wrote it pretty fast and going by memory but in general it should work.
There are some problems in your program:
Do not use gets(), it may cause buffer overflows.
Always use type int to store the return value of fgetc() in order to distinguish EOF from regular byte values.
You pass an extra argument ".txt" to printf(). It will be ignored but should be removed nonetheless.
To copy a range of lines from source to destination, you can just modify your function this way:
#include <stdio.h>
#include <string.h>
#include <errno.h>
void cp(char source_file[], char destination_file[], int start_line, int end_line) {
int ch;
int line = 1, lines_copied;
FILE *source, *destination;
source = fopen(source_file, "r");
if (source == NULL) {
printf("Cannot open input file %s: %s\n",
source_file, strerror(errno));
exit(EXIT_FAILURE);
}
destination = fopen(destination_file, "w");
if (destination == NULL) {
printf("Cannot open output file %s: %s\n",
destination_file, strerror(errno));
fclose(source);
exit(EXIT_FAILURE);
}
while ((ch = fgetc(source)) != EOF) {
if (line >= start_line && line <= end_line) {
fputc(ch, destination);
}
if (ch == '\n') {
line++;
}
}
lines_copied = 0;
if (line > start_line) {
if (line >= end_line) {
lines_copied = end_line - start_line + 1;
} else {
lines_copied = line - start_line + 1;
}
}
printf("Copied lines %d from %s to %s\n",
lines_copy, source_file, destination_file);
fclose(source);
fclose(destination);
}
int main() {
char source_file[80];
char destination_file[80];
int start_line, end_line;
printf("-Enter the name of the source file ending in .txt\n"
"-Enter the name of the destination file ending in .txt\n"
"-Enter the start and end line\n\n");
printf(">subcopy.o ");
if (scanf("%79s", source_file) != 1) {
return 1;
}
printf("destination file-> ");
if (scanf("%79s", destination_file) != 1) {
return 1;
}
printf("Start and end lines: ");
if (scanf("%d %d", &start_line, &end_line) != 2) {
return 1;
}
cp(source_file, destination_file, start_line, end_line);
return 0;
}
I have a directory say A, into which i have sub-directories aa,bb,cc,dd,ee,ff. Each sub directories have a number of .txt, .bin, .dat files. What I want to do is, check each of the sub-directory to see if it contains a text file, if yes return the sub directory name.
The below c script lists the sub directories, but please assist to check within the sub-directory for a txt file.
I'm trying to do this in windows 7-visual studio 2010
#include <dirent.h>
#include <stdio.h>
int main(void)
{
DIR *d;
DIR *f;
struct dirent *dir;
d = opendir("C:\\Users\\xp\\Desktop\\Star1");
if (d) {
while ((dir = readdir(d)) != NULL) {
if (dir->d_name[0] != '.') {
f=opendir(dir->d_name);
if (strstr(dir->d_name , ".txt")) {
printf("%s\n", dir->d_name);
}
}
}
closedir(d);
}
return(0);
}
You could use a flag. If you find a file ending in ".txt" then you set the flag and exit the loop. After the loop you check the flag.
One way to check if a string ends with a specific sub-string:
static const char string_to_find[] = ".txt";
...
// First make sure the filename is long enough to fit the name-suffix
if (strlen(dir->d_name) > strlen(string_to_find))
{
// +strlen(dir->d_name) to get a pointer to the end of dir->d_name
// -strlen(string_to_find) to get a pointer to where the suffix should start
if (strcmp(dir->d_name + strlen(dir->d_name) - strlen(string_to_find),
string_to_find) == 0)
{
// File-name ends with ".txt"
}
}
As an alternative, lazy and Windows-specific solution, you can just let the job to the windows for command this way:
#include <stdio.h>
#include <string.h>
#define MAX_LENGTH 1024
int main()
{
char buffer[MAX_LENGTH];
FILE *f = _popen("cmd /c #for /R C:\\Users\\xp\\Desktop\\Star1\\ %i in (.) do #if exist \"%~i\"\\*.txt echo %~fi 2> NUL", "r");
if (f != NULL)
{
while (fgets(buffer, MAX_LENGTH, f) != NULL)
{
int len = strlen(buffer);
if (buffer[len - 1] == '\n')
{
buffer[--len] = '\0';
}
printf("Found: %s\n", buffer);
}
_pclose(f);
}
}
Edit: fixed answer to give directory list instead of .txt files.
Instead of printing the directories you could just put it in an if-statement to check if it's the desired file. If it is: return the directory name, else continue. You can put it all in a for-loop so you can check every directory.
For example:
If(!strcmp(filename, filetofind))
Return dirname