Comparing user input with text in a file - c

The below code can output what is inside my file. I am trying to find a way to compare if the user input word/character is included in the text file. For instance, if the user writes "r" then, the program finds all the words that have an "r" in the file and output them. After that, I want to replace this word with something, so instead of "r", make it "k". For example, "roadtrip" --> "koadtrip".
The text file has a lot of words line by line , a screenshot of a small part
#define MAX 1024
int main() {
FILE* myFile = fopen("C:\\Users\\Luther\\Desktop\\txtfiles\\words.txt", "r+");
char inputWord[MAX];
char lineBuffer[MAX];
if (myFile1 == NULL)
{
printf("File Does Not Exist \n");
return 1;
}
printf("Enter the word \n");
fgets(inputWord, MAX, stdin);
while (!feof(myFile1))
{
char lineBuffer[1024];
fscanf(myFile1, "%1024[^\n]\n", lineBuffer);
//printf("%s\n", lineBuffer);
while (fgets(lineBuffer, MAX, myFile)) {
if (strstr(lineBuffer, inputWord))
puts(lineBuffer);
}
}
}
I 've managed to make it work and now the program outputs regarding the user input. If a word is the same or part of it is found in the text file, then it prints the word. Look the screenshot below:
Now I am looking for a way to replace the word. For instance, in this specific situation, the word the user inputted is "es" and then all the words that have "es" as a part of them are printed. Is there a way that I can replace for all occasions the "es" and make it "er". Then save the changes in another file without changing anything in the original file.

Here is something to use as a start:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
int main (int argc, char *argv[])
{
FILE *fp = fopen(argc > 1 ? argv[1] : "/etc/motd", "r");
char *line = NULL;
char *p = NULL;
char *needle = argv[2];
char *replace = argv[3];
size_t len = 0;
ssize_t read;
assert(fp);
while ((read = getline(&line, &len, fp)) != -1) {
if (line[0] != '#') {
if ((p = strstr(line, needle))) {
printf("%.*s%s%s", (int)(p - line), line, replace, p+strlen(replace));
} else {
printf("%s", line);
}
}
}
free(line);
fclose(fp);
return 0;
}
Note: this may not handle all edge cases. Also writing back to a file or renaming to
original is left as an exercise :)

Some other starting point
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<regex.h>
#include<sys/types.h>
int main (){
//open file
FILE *file_pointer = fopen("./test_txt.txt", "r");
const char* search_for = "3_hau_gbs";
int line_number = 1;
char* line = NULL;
size_t len = 0;
regex_t regex;
int failed = regcomp(&regex, search_for, REG_EXTENDED);
//You are serching bitwise, so you must first semicompile it
if(failed){
regfree(&regex);
} else {
while(getline(&line, &len, file_pointer) != -1){
//go line after line and check if it include the word you
//you are looking for
int match = regexec(&regex, line, 0, NULL, 0);
if(!match){
//when find so output
printf("%d:%s",line_number, line);
}
line_number++;
}
if(line){
free(line);
}
regfree(&regex);
fclose(file_pointer);
}
}

Related

Need help parsing data from .csv file C

I have the following .csv file containing information about the song, artist, release year (if specified) and number of listens:
Look What The Cat Dragged In,Poison,,Look What The Cat Dragged In by Poison,1,0,1,0
Nothin' But A Good Time,Poison,1988,Nothin' But A Good Time by Poison,1,1,21,21
Something To Believe In,Poison,1990,Something To Believe In by Poison,1,1,1,1
Talk Dirty To Me,Poison,1978,Talk Dirty To Me by Poison,1,1,1,1
A Salty Dog,Procol Harum,1969,A Salty Dog by Procol Harum,1,1,1,1
A Whiter Shade of Pale,Procol Harum,1967,A Whiter Shade of Pale by Procol Harum,1,1,3,3
Blurry,Puddle of Mudd,2001,Blurry by Puddle of Mudd,1,1,1,1
Amie,Pure Prairie League,,Amie by Pure Prairie League,1,0,4,0
Another One Bites the Dust,Queen,1980,Another One Bites the Dust by Queen,1,1,102,102
Bicycle Race,Queen,1978,Bicycle Race by Queen,1,1,3,3
Kiss You All Over,Kiss,1978,Kiss You All Over by Kiss,1,1,5,5
The name of the file and the desired year should be given as command line arguments, and the program should print all songs from that specific year.
e.g.: ./a.out music.csv 1978
Output:
Talk dirty to me
Bicycle Race
Kiss You All Over
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX 300
typedef struct {
char song[101], *artist, *line;
long int year;
} music;
int checkYear(char *word)
{
for (int i = 0; i < strlen(word); i++) {
if (!isdigit(word[i]))
return 0;
}
return 1;
}
int main(int argc, char **argv)
{
FILE *fin = fopen(argv[1], "r");
if (!fin)
{
printf("Error opening the file.\n");
return 1;
}
char buf[MAX];
//int nLines = 0; //count the number of lines
//music *array = NULL;
while( fgets(buf, MAX, fin))
{
buf[strcspn(buf, "\n")] = '\0'; // strip the trailing newline
char *word = strtok(buf, ",");
while (word)
{
//printf("Word is : %s\n", word);
if (checkYear(word))
{
//printf("Year : %s\n", word);
music *array = (music *)malloc(sizeof(music));
char *p;
array->year = strtol(word, &p, 10);
if (array->year == atoi(argv[2]))
{
//printf("Year : %ld\t%d\n", array->year, atoi(argv[2]));
if (scanf("%100[^,]", array->song) == 1)
{
printf("Song : %s\n", array->song);
}
}
}
word = strtok(NULL, ",");
}
}
//printf("I've read %d lines\n", nLines);
fclose(fin);
return 0;
}
So far, it's going decent, I can extract the specified year from each line, but now I just need to print the name of the song from those lines (the first token on the line). I thought about using scanf("%[^,]") to read and print everything up until the first comma but it's just stuck in an endless loop. Could you give me an idea? Thanks in advance!
There are multiple problems in the code:
you do not check that enough arguments were passed on the command line, potentially invoking undefined behavior if not.
you do not need to allocate a music structure: you can just parse the first 3 fields, check the year and output the name of the song directly.
strtok() is inappropriate to split fields from a csv file because it treats a sequence of separators as a single separator, which is incorrect and causes invalid parsing if some fields are empty.
sscanf("%[^,]", ...) will fail to convert an empty field.
To split the fields from the csv line, I recommend you use a utility function that behaves like strtok_r() but tailored for csv lines. A simplistic version will stop on , and \n and replace these with a null byte, returning the initial pointer and updating the pointer for the next field. A more advanced version would also handle quotes.
Here is a modified version:
#include <stdio.h>
#include <string.h>
#define MAX 300
char *get_field(char **pp) {
char *p, *start;
for (p = start = *pp; *p; p++) {
if (*p == ',' || *p == '\n') {
*p++ = '\0';
break;
}
}
*pp = p;
return start;
}
int main(int argc, char *argv[]) {
char buf[MAX];
FILE *fin;
char *filename;
char *select_year;
if (argc < 3) {
printf("Missing arguments\n");
return 1;
}
filename = argv[1];
select_year = argv[2];
fin = fopen(filename, "r");
if (!fin) {
printf("Error opening the file %s.\n", filename);
return 1;
}
while (fgets(buf, sizeof buf, fin)) {
char *p = buf;
char *song = get_field(&p);
char *artist = get_field(&p);
char *year = get_field(&p);
if (!strcmp(year, target_year)) {
printf("%s\n", song);
}
}
fclose(fin);
return 0;
}
regarding: scanf("%[^,]") this consumes (upto but not including) the comma.
So the next instruction needs to be something like getchar() to consume the comma. Otherwise, on the next loop nothing will be read because the first character in stdin is that same comma.

fgets is returning a blank screen

I am new to C, this is my first project and have been teaching myself. Within my program, one of my functions needs to read a line from a file, and store it in a char array. When I trace the program with gdb the array (line[]) is simply zeros. This leads to my program returning the error "Error: a line in the asset file lacks a ':' separator\n"
Here is my code:
//return the line number (0 based) that the cmd is on, -1 if absent
int locateCmd(char cmd[]) {
int lineIndex = -1; //-1, because lineIndex is incramented before the posible return
char cmdTemp[10] = "\0";
//create a compareable cmd with correct cmd that has its remaining values zeroed out
char cmdCmp[10] = "\0";
memset(cmdCmp, 0, sizeof(cmdCmp));
for (int i = 0; i < strlen(cmd); i++) {
cmdCmp[i] = cmd[i];
}
FILE *file = fopen(ASSET_FILE, "r");
//loop until target line is reached
while (strcmp(cmdTemp, cmdCmp) != 0) {
//check if last line is read
if (lineIndex == lineCounter(file)-1) {
return -1;
}
memset(cmdTemp, 0, sizeof(cmdTemp));
char line[61];
fgets(line, 61, file);
//set cmdTemp to the command on current line
lineIndex++;
for (int i = 0; line[i] != ':'; i++) {
cmdTemp[i] = line[i];
//return error if line doesn't contain a ':'
if (line[i] = '\n') {
printf("Error: a line in the asset file lacks a ':' separator\n");
exit(1);
}
}
}
return lineIndex;
}
Some context, this function is passed a command, and its job is to read a document that appears like this:
command:aBunchOfInfoOnTheComand
anotherCommand:aBunchOfInfoOnTheComand
and pick out the line that the passed command (cmd[]) is stored on.
The issue is with the fgets on line 24. I have separated the relevant portion of this code out into a smaller test program and it works fine.
The test program that works is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char *argv[]) {
FILE *file = fopen("tutorInfo.txt", "r");
char line[61];
fgets(line, 61, file);
printf("%s\n", line);
}
The proper exicution of my test program leads me to believe other code in my function is causing the issue, but i'm not sure what. It may be important to note, the problematic code has the same imports as my sample program. Any help would be much appreciated.
As OP didn't provide a Minimal, Complete, and Verifiable example, I have to base my answer on the functional description provided in the question.
I already covered some error and corner cases, but I'm sure I missed some. The approach is also inefficient, as the file is read over and over again, instead of parsing it once and returning a hash/map/directory for easy lookup. In real life code I would use something like GLib instead of wasting my time trying to re-invent the wheel(s)...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LINE_BUFFER_LENGTH 200
unsigned int locateCmd(FILE *fh, const char *key, const char **cmd_line) {
unsigned int found = 0;
size_t key_length = strlen(key);
*cmd_line = NULL;
/* make sure to start read from start of file */
rewind(fh);
unsigned int line_no = 0;
static char buffer[LINE_BUFFER_LENGTH];
while (!feof(fh) && (found == 0)) {
// NOTE: EOF condition will be checked on the next iteration
fgets(buffer, sizeof(buffer), fh);
size_t length = strlen(buffer);
line_no++;
if (buffer[length - 1] != '\n') {
printf("line %u is too long, aborting!\n", line_no);
return(0);
}
if ((strncmp(key, buffer, key_length) == 0) &&
(buffer[key_length] == ':')) {
found = line_no;
buffer[length - 1] = '\0'; // strip line ending
*cmd_line = &buffer[key_length + 1];
}
}
return(found);
}
int main(int argc, char *argv[]) {
FILE *fh = fopen("dummy.txt", "r");
if (!fh) {
perror("file open");
return(1);
}
int ret = 0;
while (--argc > 0) {
const char *cmd;
const char *key = *++argv;
unsigned line_no = locateCmd(fh, key, &cmd);
if (line_no != 0) {
printf("key '%s' found on line %u: %s\n", key, line_no, cmd);
ret = 0;
} else {
printf("key '%s' not found!\n", key);
};
}
if (fclose(fh) != 0) {
perror("file close");
return(1);
}
return(ret);
}
Test input dummy.txt:
command:aBunchOfInfoOnTheComand
anotherCommand:aBunchOfInfoOnTheComand
brokenline
foo:bar
toolong:sadflkjaLKFJASDJFLKASJDFLKSAJ DLFKJ SLDKJFLKASDFASDFKJASKLDJFLKASJDFLKJASDLKFJASLKDFJLKASDJFLKASJDLFKJASDKLFJKLASDJFLKSAJDFLKJASDLKFJKLASDJFLKASJDFKLJASDLKFJLKASDJFLKASJDFLKJSADLKFJASLKDJFLKC
Some test runs:
$ gcc -Wall -o dummy dummy.c
$ ./dummy command foo bar
key 'command' found on line 1: aBunchOfInfoOnTheComand
key 'foo' found on line 5: bar
line 6 is too long, aborting!
key 'bar' not found!

Extract a column of a list from a text file in C

This may be a simple question with a straightforward answer, but searching through the site I didn't find anything (maybe because I'm new to C programming) except for python codes, which I already wrote and it's quite inefficient.
Suppose I have a list of data in a timestamps.txt file, formatted like this:
<large integer>, <integer between 1 and 8>
<large integer>, <integer between 1 and 8>
and so on (the file is about 4GB)...
What I want to do is to copy only the second column to another file, say singles.txt.
What I did so far works, but it's a rather naive approach and takes too much time. Here's my code:
int main(int argc, char const *argv[])
{
FILE *input_file;
FILE *output_file;
char ch;
int check = 0;
input_file = fopen("timestamps.txt","r");
output_file = fopen("singles.dat","w");
if (!input_file)
return -1;
while((ch = getc(input_file))!=EOF)
{
if(check==1)
{putc(ch,output_file);putc('\n',output_file);}
if(ch == ',')
check = 2;
else
check -= 1;
}
fclose(input_file);
fclose(output_file);
return 0;
}
I'm sure there is a faster way, but I can't seem to make anything work.
Any help would be much appreciated.
Using fgets and fputs is faster than multiple calls to getc and putc, all you need is a buffer (a little buffer in this case) to store the current line:
int main(int argc, char const *argv[])
{
FILE *input_file;
FILE *output_file;
char buf[128];
char *ptr;
input_file = fopen("timestamps.txt","r");
output_file = fopen("singles.dat","w");
if (!input_file)
return -1; /* use EXIT_FAILURE instead of -1 */
/* you forget to check output_file */
while (fgets(buf, sizeof buf, input_file)) {
ptr = strchr(buf, ','); /* find the comma */
if (ptr != NULL) {
fputs(ptr + 1, output_file); /* +1 to skip the comma */
}
}
fclose(input_file);
fclose(output_file);
return 0;
}
Your idea isn't so bad, but you should make your variable check either 0 or 1, depending on whether you want to copy the current input data or not. And you must reset the check with each new line.
Alternatively, you could countthe current field your are in and copy the data when the field is the one you want.
Here's a version that copies the column want delimited by sep verbatim to the output file:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
FILE *in = stdin;
FILE *out = stdout;
int want = 1;
int col = 0;
int sep = ',';
for (;;) {
int c = getc(in);
if (c == EOF) break;
if (c == sep) {
col++;
} else if (c == '\n') {
col = 0;
putc(c, out);
} else if (col == want) {
putc(c, out);
}
}
return 0;
}
(I've used stdin and stdout, because I was lazy and didn't want to do the flie opening and closing stuff.)

How do I find a string in a file?

I'm new at C and recently finished my work with files. I tried to create a program which will find an entered name in a file but it does not work. Could you try to repair it? I'll be thankful.
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fr;
int i,p,counter,final,length,j,c,k = 0;
char name[256];
char buffer[1024];
fr = fopen("C:/Users/prixi/Desktop/HESLA.TXT","r");
while ((c = fgetc(fr)) != EOF)
counter++;
printf("Enter the name");
scanf("%s",&name);
length = strlen(name);
while (fscanf(fr, " %1023[^\n]", buffer) != EOF) {
for (i = 0; i <= counter; i++)
if (name[0] == buffer[i]){
for (j = 0;j < length; j++ )
if (name[j] == buffer[i+j])
p++;
else
p = 0;
/* The 2nd condition is there because after every name there is ' '. */
if (p == length && buffer[i+j+1] == ' ')
final = 1;
}
}
if ( final == 1 )
printf("its there");
else
printf("its not there");
return 0;
}
It loads the inside of the file to the buffer and then scans char by char depending on how long the file is. I know that it's inefficient and slow, but I have been learning C only for like 4 days. I would really like you to help me fixing my own code otherwise :D I probably wont be able to fall asleep.
There are a lot of way to search a string into a File.
Try this:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char *loadFile(const char *fileName);
int main (void) {
const char *fileName = "test.txt";
const char *stringToSearch = "Addams";
char *fileContent = loadFile(fileName);
if (strstr(fileContent, stringToSearch)){
printf("%s was Found\n",stringToSearch);
}else{
printf("%s was not Found\n",stringToSearch);
}
free(fileContent);
return 0;
}
char *loadFile(const char *fileName){
size_t length,size;
char *buffer;
FILE *file;
file = fopen (fileName , "r" );
if (file == NULL){
printf("Error fopen, please check the file\t%s\n",fileName);
exit(EXIT_FAILURE);
}
fseek (file , 0 , SEEK_END);
length = (size_t)ftell (file);
fseek (file , 0 , SEEK_SET);
buffer = malloc(length+1);
if (buffer == NULL){
fputs ("Memory error",stderr);
exit (2);
}
size = fread (buffer,1,length,file);
if (size != length){
fputs ("Reading error",stderr);
exit(3);
}
buffer[length] = '\0';
fclose (file);
return buffer;
}
Output:
Addams was Found
I have inside the file "test.txt" the following:
Michael Jackson
Bryan Addams
Jack Sparrow
There are multiple problems with your code. You did not post the variable definitions, so we cannot verify if they are used consistently, especially name that should be an array of char.
The main issue is this: you count the number of bytes in fr by reading it, but you do not rewind the stream before scanning it for instances of the string.

String search C program for command prompt

I wrote C program of searching string. The problem is MyStrstr() function doesn't work with
command prompt. It only works with IDE. So, can anyone advise me how to fix the code for working with command prompt. With regards...
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define ARGUMENT_COUNT 3
#define FILE_INDEX 2
#define SEARCH_INDEX 1
#define BUFFER 256
#define SUCCESS 0
#define ERRCODE_PARAM 1
#define ERRCODE_FILENAME 2
#define MSG_USAGE "String Search Program === EXER5 === by Newbie\nUsage: %s Search_String fileName"
#define MSG_ERROR "Can not open file. [%s]"
char* MyStrstr(char* pszSearchString, char* pszSearchWord);
int main(int argc, char* argv[])
{
FILE* pFile = NULL;
char szData[BUFFER];
char* pszCutString = NULL;
if(argc != ARGUMENT_COUNT) {
printf(MSG_USAGE, argv[0]);
return ERRCODE_PARAM;
}
pFile = fopen(argv[FILE_INDEX], "r");
if(pFile == NULL) {
printf(MSG_ERROR, argv[FILE_INDEX]);
return ERRCODE_FILENAME;
}
pszCutString = MyStrstr(szData, argv[SEARCH_INDEX]);
if(pszCutString != NULL) {
printf("%s", pszCutString);
}
fclose(pFile);
pFile = NULL;
return SUCCESS;
}
char* MyStrstr(char* pszSearchString, char* pszSearchWord) {
int nFcount = 0;
int nScount = 0;
int nSearchLen = 0;
int nIndex = 0;
char* pszDelString = NULL;
char cSLen = 0;
size_t len = 0;
if(pszSearchString == NULL || pszSearchWord == NULL) {
return NULL;
}
while(pszSearchWord[nSearchLen] != '\0') {
nSearchLen++;
}
if(nSearchLen <= 0){
return pszSearchString;
}
cSLen = *pszSearchWord++;
if (!cSLen) {
return (char*) pszSearchString;
}
len = strlen(pszSearchWord);
do {
char cMLength;
do {
cMLength = *pszSearchString++;
if (!cMLength)
return (char *) 0;
} while (cMLength != cSLen);
} while (strncmp(pszSearchString, pszSearchWord, len) != 0);
return (char *) (pszSearchString - 1);
}
You want to open a file, search the contents of that file for a string and return/print that. You are instead doing:
char szData[256]; // <-- making an uninitialized buffer
char* pszCutString = NULL;
pFile = fopen(argv[2], "r"); // <-- Opening a file
pszCutString = MyStrstr(szData, argv[1]); // <-- searching the buffer
if(pszCutString != NULL) {
printf("%s", pszCutString);
}
fclose(pFile); // <-- Closing the file
So you never fill your buffer szData with the contents of the file noted in argv[2]. You're trying to search an uninitialized buffer for a string. You're luck the result is just "no output comes out".
You need to take the contents of the file in argv[2] and place it in the buffer szData then do the search. This could be accomplished by adding a call to a function like read() or fscanf()
Note 1:
I assume when you say this "worked" in the IDE, the code was a little different and you weren't using the command line arguments.
Note 2:
you should also check to fopen() worked before trying to read from/close pFile, and if your file is possibly larger than 256 characters you will need to change your code to either have a dynamically sized string, or you'll need to loop the buffer fills (but then you have to worry about breaking a word apart), or some other mechanism to check the full file.

Resources