-1

I am facing problems scanning an existing file.

The challenge is that I have a source text file with some strings. I have to scan this file for the word "o'clock", and where I find it, I have to take the word before "o'clock" and enclose it in square brackets.

I found a function to find the trigger, but I don't understand what I need to do next. I would be grateful if you could explain how to replace the characters in the read string.

For example: We have a string:

"Let's go to the graveyard at night - at twelve o'clock!"

I need to replace the word twelve with [twelve].

Here is the code:

#include <stdio.h>
#include <string.h>

int main() {
    FILE * fp; //open exists file
    FILE * fn; //open empty file
    char c[1000]; // buffer
    char trigger[] = "o'clock"; // the word before which the characters should be added
    char name [] = "startFile.txt"; // name of source file
    char secondName[] = "newFile.txt"; // name of output file
    fp = fopen (name, "r"); // open only for reading
    if (fp == NULL) { // testing on exists
        printf ( "Error");
        getchar ();
        return 0;
    }
    fn = fopen(secondName, "w"); // open empty file for writing
    while (!feof(fp)) { // scanning all string in the source
        if(fgets(c,1000, fp) != NULL) { // reading charter from file
            fprintf(fn, c); // writing charter from buffer "c" to empty file
            if (strstr(c, trigger)) { // find triggered word
                // I tried compare string which has a trigger-word
            }
        }
    }

    fclose(fp); // closing file
    fclose(fn);
    return 0;
}
Drew Dormann
  • 50,103
  • 11
  • 109
  • 162
Dsyder
  • 13
  • 2
  • 5
    Pick one language. C or C++ (your includes are C++, your code more like C). Also read [Why is `while ( !feof (file) )` always wrong?](https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) – pzaenger Feb 04 '21 at 20:08
  • 3
    You've tagged this as C++, described it as C, and you're using `FILE*` (C) and `using namespace std` (C++). This code is having a severe identity crisis. – tadman Feb 04 '21 at 20:12
  • Sound like joke, but I have to using libraries from C in C++ program – Dsyder Feb 04 '21 at 20:12
  • 2
    Tip: When using C++ **use C++**. Use `std::string` instead of `char*`. Use [`ifstream`](https://en.cppreference.com/w/cpp/io/basic_ifstream) instead of `FILE*`. Turn on compiler warnings that point out things like using `char*` when you should be using `const char*`. – tadman Feb 04 '21 at 20:13
  • Sounds like you're stuck in one of those completely useless "C++" courses that teach you nothing but bad habits and how programming is endless misery because they miss the point of pretty much everything. Hope you survive without too much damage! – tadman Feb 04 '21 at 20:14
  • "C with nullptrs"? – Drew Dormann Feb 04 '21 at 20:15
  • @Dsyder you may do better getting a C compiler to build your code and then asking a [c] tagged question. This is 99% C code that you're asking the C++ crowd to fix. – Drew Dormann Feb 04 '21 at 20:17
  • @Dsyder You are clearly using C++ (C++ includes, C++ language features), so I strongly suggest you change to C++ features (`std::string`, `std::ifstream`, etc), that will make reading lines, and searching for and replacing substrings, MUCH easier. Trying to do this with C-style strings is going to be more difficult. – Remy Lebeau Feb 04 '21 at 20:17
  • `fprintf(fn,c)` is bad because it will trying to scan `c` for (e.g. `%d`, etc.). Better to do: `fprintf(fn,"%s",c)` or `fputs(c,fd)` _But_, you really don't want to output the _unmodified_ buffer at that point. – Craig Estey Feb 04 '21 at 20:18
  • @Dsyder C or not, both languages can be inspected what every statement exactly does [using your debugger](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems), I believe it's time for you to learn that essential skill. – πάντα ῥεῖ Feb 04 '21 at 20:19
  • You have to write a lexer that outputs the following lexemes: word, spaces, eof. The rest is easy to figure. – zdf Feb 04 '21 at 20:20
  • You're not doing yourself many favors. *"I found a function to find the trigger"* - its not about finding things at this point; it's about *designing and writing* them. Ex: your task is to find the trigger and modify the word *before* it. So what good is it doing you to dump the entire line, *then* search for the trigger word? Wouldn't that (searching and possibly modifying the line) be something you would want to do *before* writing any output for that line? I think you need to work out, in detail, what you want to do *before* coding it. – WhozCraig Feb 04 '21 at 20:21
  • Omg, I'm wildly sorry, but it's not my desire to use C in C ++, but the university. Sorry for such a terrible question – Dsyder Feb 04 '21 at 20:21
  • @Dsyder I have made the small adjustments necessary to make your program completely C. Whatever solutions offered should port back to your original code. – Drew Dormann Feb 04 '21 at 20:27
  • @Dsyder The advice of the other commenters is designed to help you become better at designing software - not to help you get a good mark on this assignment. Probably your instructor would not be happy if you used C++ features or they would have taught you about them already and you would have used them. So, for this assignment, you probably need to do it the way you are already doing it. But that doesn't help you get an answer from people who are trying to help you get better at programming. So, now that Drew has changed the code to be completely C you should be able to get some actual help. – Jerry Jeremiah Feb 04 '21 at 20:27
  • Does this do what you want? https://onlinegdb.com/H18Tgy9xu – Jerry Jeremiah Feb 04 '21 at 21:00
  • @JerryJeremiah , oh, mate, thx so much, this is what was needed – Dsyder Feb 04 '21 at 21:04
  • @JerryJeremiah This may be sufficient. But, perhaps you could loop on the `strstr` so that it can handle _multiple_ `o'clock` strings in the same buffer (e.g.) `Let's go to the graveyard at twelve o'clock and have dinner at six o'clock beforehand!` – Craig Estey Feb 04 '21 at 21:08
  • @CraigEstey good idea. I made only the minimum necessary changes to get what he had already written to work. I think that is a lot more instructive than changing a bunch of stuff and then the OP has to try to figure out which changes fixed his actual question and which were extras. However, I do like your idea and I will edit my answer. – Jerry Jeremiah Feb 04 '21 at 21:16

1 Answers1

1

In your code, you copy the line to the output file and then, afterwards, look for the trigger word. But the goal is to find the trigger and edit the string, and only then write the line to the output file. Also, in order to edit the line, you need to know where the trigger was found so you need to save the output of the search.

So, instead of:

    fprintf(fn, c); // writing charter from buffer "c" to empty file
    if (strstr(c, trigger)) { // find triggered word
        // I tried compare string which has a trigger-word
    }

We need:

    char *p = strstr(c, trigger);
    if (p) {
        // I tried compare string which has a trigger-word
    }
    fputs(c, fn); // writing charter from buffer "c" to empty file

So now we found the trigger and we need to search backwards to find the end and then the start of the word before it. When we find them we need to create a spot and insert the edits:

    char *p = strstr(c, trigger);
    if (p) {
        // ok we found the trigger - now we need to search backwards
        // to find the end and then the start of the word before
        while (p>c && p[-1]==' ') p--; // find the end of the word
        memmove(p+1, p, c+999 - p); // make a space for the ]
        *p = ']';
        while (p>c && p[-1]!=' ') p--; // find the start of the word
        memmove(p+1, p, c+999 - p); // make a space for the [
        *p = '[';
    }
    fputs(c, fn); // writing charter from buffer "c" to empty file

Try it here: https://www.onlinegdb.com/H18Tgy9xu

But then, what if there was more than one "o-clock" in the string? Something like this:

Let's go to the graveyard at twelve o'clock and have dinner at six o'clock beforehand!

In that case, you need to loop around until you have found all of them:

Here is the final code:

#include <stdio.h>
#include <string.h>

int main() {
    FILE * fp; //open exists file
    FILE * fn; //open empty file
    char c[1000]; // buffer
    char trigger[] = "o'clock"; // the word before which the characters should be added
    char name [] = "startFile.txt"; // name of source file
    char secondName[] = "newFile.txt"; // name of output file
    fp = fopen (name, "r"); // open only for reading
    if (fp == NULL) { // testing on exists
        printf ( "Error");
        getchar ();
        return 0;
    }
    fn = fopen(secondName, "w"); // open empty file for writing
    if (fn == NULL) { // testing on create
        printf ( "Error");
        getchar ();
        return 0;
    }
    while (fgets(c, 1000, fp)) { // scanning all string in the source
        char *p = c; // we need to start searching at the beginning
        do {
            p = strstr(p+1, trigger); // find the next oclock after this one
            if (p) {
                // ok we found the trigger - now we need to search backwards
                // to find the end and then the start of the word before
                while (p>c && p[-1]==' ') p--; // find the end of the word
                memmove(p+1, p, c+999 - p); // make a space for the ]
                *p = ']';
                while (p>c && p[-1]!=' ') p--; // find the start of the word
                memmove(p+1, p, c+999 - p); // make a space for the [
                *p = '[';
                p = strstr(p, trigger); // find that same oclock again
                // (we couldn't save the location because memmove has changed where it is)
            }
        } while (p); // as long as we found one, search again
        fputs(c, fn); // writing charter from buffer "c" to empty file
    }
    fclose(fp); // closing file
    fclose(fn);
    return 0;
}

Try it here: https://onlinegdb.com/SJMrP15ld

Jerry Jeremiah
  • 7,408
  • 2
  • 21
  • 27
  • To make life easy, you could post the entire godbolt code as a single downloadable/runnable code block. It's well commented enough to do that. – Craig Estey Feb 04 '21 at 21:14
  • @CraigEstey Good idea, Actually, I always do that - I have no idea why I didn't do it this time. :( – Jerry Jeremiah Feb 04 '21 at 21:17
  • What I do [sometimes] is similar to what you've done. Some explanatory text interspersed with some small code blocks. At the bottom, I post the full code. If OP is learning [vs. an experienced coder with a specific issue], I usually do a three stage approach. (1) Minimal changes to OP's code to fix bug (2) Enhancements (e.g. the muliple o'clock thing) (3) Full cleanup to what I would do if I wrote it from scratch. A lot of OP's have said they like that approach. I'll usually post at step (1) and edit the answer later for (2)/(3). YMMV – Craig Estey Feb 04 '21 at 21:26
  • @Dsyder It won't work. The trigger is defined as a word. The text to be bracketed is defined as the previous word. The above code does not search for words. It searches for substrings. – zdf Feb 04 '21 at 21:34
  • @zdf This is exactly what I need for the assignment: scan substring and add square brackets – Dsyder Feb 04 '21 at 21:43
  • @Dsyder Just to be clear: If the trigger is `is` and the source is `This house`, the result is `[Th]is house`. `Th` is a fragment, not a word. – zdf Feb 04 '21 at 21:43
  • @Dsyder _"This is exactly..."_ I understand, but in this case the problem definition is wrong. Good luck! – zdf Feb 04 '21 at 21:45
  • @zdf Of course, your solution is much more detailed. But this is a one-time task and the trigger-word will not change – Dsyder Feb 04 '21 at 21:46