Home > Back-end >  How do I find the line number and line item number in a text file?
How do I find the line number and line item number in a text file?

Time:04-22

I need to find the line number and position number in the line with the specified character in the text file. I did this, but found the last symbol. I have a task to find the fourth point "." from the end of the text file, how is this possible?

Text file:
One One
Two
....
Three
..
Three

Now I get the following result:
Line: 2
Pos: 6

What I have tried:
Check out my code (I'm looking for the first dot from the end of the file):

#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main()
{
    ifstream fin("t.txt");
    string line;

    int lines = 0;
    int characters = 0;

    int found_line = 0;
    size_t found_pos = string::npos;

    while (true)
    {
        if (!getline(fin, line)) break;
        auto pos = line.find_last_of('.');
        if (pos != string::npos)
        {
            found_line = lines;
            found_pos = (characters   pos);
        }
          lines;
        characters  = line.length();
    }

    if (found_pos != string::npos)
    {
        found_line = (lines - 0 - found_line); // line no, counting backwards from the bottom of the file
        found_pos = (characters - 0 - found_pos); // pos of the 'first' dot, counting backwards from the end of the file
        cout << "line " << found_line << "\n";
        cout << "pos " << found_pos << "\n"   1;
    }
    return 0;
}

CodePudding user response:

In my comment, I sketched the following possible algorithm to solve OPs issue:

What you could do: While reading the file you can count the line numbers. Scan the line for one (or multiple) .s. The results can be stored in an array of 4 entries. (You have to manage an index for this.) For each found ., you store the line and inc. the index modulo 4 (e.g. i = (i 1) % 4;. When you reached the end of file, the (i 1) % 4 will contain the line for the 4th . from end. Of course, you also have to count that you found at least 4 .s (to handle edge cases).

Just for fun, I made an MCVE on coliru:

#include <array>
#include <fstream>
#include <iostream>
#include <string>

// (a kind of) ring buffer
template <typename V, int N>
struct RingT {
  std::array<V, N> values;
  int i = 0;
  int n = 0;
  
  void push(const V& value)
  {
    values[i  ] = value;
    if (i >= N) i = 0;
    if (n < N)   n;
  }
  
  int size() const { return n; }
  
  const V& operator[](int j) const { return values[((i - 1   j) % n   n) % n]; }
};

// text coordinates
struct Coord {
  int line, col;
};

int main()
{
  const int N = 4; // the number of occurrences (from end) to count
  const char C = '.'; // the character to count occurrences for
  // use (a kind of) ring buffer to store text coordinates
  RingT<Coord, N> coords;
  // scan file for occurrences of character C
#if 0 // OP
  std::ifstream fIn("t.txt");
#else // for online check
  std::ifstream fIn("main.cpp");
#endif // 0
  int line = 1;
  for (std::string buffer; std::getline(fIn, buffer);   line) {
    for (size_t col = 0;;   col) {
      col = buffer.find(C, col);
      if (col < buffer.size()) coords.push({ line, (int)col });
      else break;
    }
  }
  // output result
  if (coords.size() < N) {
    std::cerr << "ERROR! File didn't contain " << N << " occurrences of '" << C << "'.\n";
  } else {
    const Coord& coord = coords[1]; // equivalent to coords[N - 1] -> it's a ring
    std::cout << "The " << N << "th occurrence of '" << C << "' from end"
      << " was in line " << coord.line << ", col. " << coord.col << '\n';
  }
}

Output:

The 4th occurrence of '.' from end was in line 52, col. 85

To simplify things on coliru, I used the source code main.cpp instead of t.txt. The online viewer of coliru displays line numbers. Hence, it's quite easy to check that the 4th occurrence of . from end is in fact in line 52. Line 52 is this one:

    std::cerr << "ERROR! File didn't contain " << N << " occurrences of '" << C << "'.\n";

(I must admit that I didn't check the column but 85 looks reasonable.—There is a . near the end of line.)

The complete code is actually quite simple and straight-forward. The most convoluted thing is probably the RingT::operator[]:

const V& operator[](int j) const { return values[((i - 1   j) % n   n) % n]; }

Thus, I'd like to dissect and explain it a bit:

  1. There is given the index j as argument which should be looked up.
  2. The index is relative to the last entry which was done: i - 1 j.
  3. The index is used modulo n to wrap around inside the buffer: (i - 1 j) % n.
  4. If i - 1 j was a negative value then (i - 1 j) % n becomes a negativ value as well. To fix this, n is added and then again modulo n: ((i - 1 j) % n n) % n.

This grants that the resulting index for array access will always be in the range [0, n). (Admittedly, possible integer overflow issues are ignored.)

  •  Tags:  
  • c
  • Related