1

I've got an assignment for my Software Engineering class that's driving me bananas. I've been asked to design a line counter that counts only the logical lines of code for any given file. It must omit blank lines and comments.

I've got the code pretty much working except for that it over counts the line numbers by 2 lines no matter what file I pass into it. I can't for the life of me see where my problem is and was wondering if anyone could help me out.

Here's my code:

#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <stdio.h>

using namespace std;

int main () {

    // Initialize variables
    ifstream infile;
    string filename;
    int line = 0;

    // Get file input
    cout << "Enter the filename" << endl;
    cin >> filename;

    // open the file
    infile.open(filename.c_str());

    // read the lines and skip blank lines and comments
    while(getline(infile, filename)) {
        if(filename.empty() || filename.find("//") == true) {
            continue;
        }

        // increment the line number
        ++line;
    }

    // close the file
    infile.close();

    // display results
    cout << "There are " << line << " lines of code in this file." << endl;
}

The counter reads as follows in the terminal: "There are 24 lines of code in this file."

According to my calculations there should only be 22 lines of logical code. Help would be appreciated.

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
n_alvarez2007
  • 11
  • 1
  • 2
  • You don't need to do `infile.open(filename.c_str())`. Simply `infile.open(filename)` will do. – Emil Laine Feb 07 '15 at 06:45
  • @zenith: Depends on the version of the standard library. The `std::string` overload was added long after it should have been. – Ben Voigt Feb 07 '15 at 06:47
  • 2
    Also you shouldn't use functions if you don't know how to use them. `string::find` returns the position of the argument if it was found in the string and `npos` if it wasn't found, not a boolean value. – Emil Laine Feb 07 '15 at 06:48
  • 1
    You are ignoring lines like `++i; // increment i`. You are counting lines that contain only whitespace. – danielschemmel Feb 07 '15 at 06:49
  • @zenith infile.open(filename) returns an error. I'm working with a GCC compiler and the C++11 standard library. Not sure if this makes a difference but I was taught to add .c_str() at the end of a string name when trying to pass it through the ifstream file. – n_alvarez2007 Feb 07 '15 at 07:27
  • Memory is cheap - don't confuse yourself by using filename to represent both the filename and the data. Use a different variable for the data. You will save yourself a lot of grief in the future. Also, you don't need cstring or stdio.h – cup Feb 07 '15 at 08:05

3 Answers3

2

Why don't you add a print statement such as cout << filename << '\n'; to identify the lines it is identifying? I suspect you'll see a few blank lines.

I suspect you need to trim the whitespace out of your strings. You probably have some blank lines that contain spaces or tabs. Hence, they are not technically empty as far as str::empty is concerned.

Also, by doing the trim and fixing the other bug I see in your code involving treating the '//' as a comment.

Hence, it becomes a simple fix with the trims.

while(getline(infile, filename)) {

    filename = ltrim(filename);  // remove leading whitespace
    filename = rtrim(filename);  // remove trailing whitespace

    if(filename.empty() || (filename.find("//") == 0)) {
        continue;
    }

    // increment the line number
    ++line;
}

You can find the implementations of rtrim and ltrim on this other SO answer here.

Community
  • 1
  • 1
selbie
  • 100,020
  • 15
  • 103
  • 173
  • This returns a ton of errors for me. First of all ltrim and rtrim return as invalid because they aren't declared in the scope of my program. I attribute this to not declaring the libraries they belong to though. – n_alvarez2007 Feb 07 '15 at 07:29
  • @n_alvarez2007 - I'm not going to do your homework for you. Stack Overflow is not your own personal Mechanical Turk. The `ltrim` and `rtrim` functions you will need to copy from the link I gave you at the bottom of my answer. – selbie Feb 08 '15 at 01:44
  • would be better if compare find result like this: filename.find(foo) != string::npos – Y00 Mar 14 '21 at 08:28
  • 1
    @Y00 - that would mistakenly exclude lines that have both code and statements. For example: `area = PI*radius*radius; // compute the area of the circle` would get excluded even though it has valid code. – selbie Mar 14 '21 at 09:34
0

Replace filename.find("//") == true with filename.find("//") == 0 to find lines that start with //, so that it won't think that lines like int i = 0; // comment are non-code lines.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
  • This doesn't work. I tried your suggestion and my output went from 24 lines of code to 30 lines of code meaning it became even less accurate somewhere. – n_alvarez2007 Feb 07 '15 at 07:26
  • @n_alvarez2007 I was not saying this is the only modification you'd have to make. – Emil Laine Feb 10 '15 at 19:00
0
  1. empty() does mean "no character at all", while you also want to filter out lines that contain only spaces ant tabs.
  2. you might have code in lines that contain // (for instance int i; // loop index, so filtering them out will get wrong results.

A sure way of doing things would be to:

  1. strip comments
  2. strip whitespaces

and then see if the remaining line is empty or not.

String handling in C/C++ is pathetic, so you'll be the 1.000.000th poor guy forced to write (or copy) the code necessary to trim spaces at both ends of a string.

btw. calling filename a variable supposed to hold the current line is no shortcut out of the banana grove.

kuroi neko
  • 8,479
  • 1
  • 19
  • 43