Question

I am attempting to write a script to count the number of lines, words, and characters input by the user in STDIN. Using the script below, I can accomplish this when a user inputs a file as a CLI, but when I attempt to use this code for STDIN, I end up with an infinite loop. What should I change to fix this?

print "Enter a string to be counted";
my $userInput = <STDIN>;
while ($userInput) {
        $lines++;
        $chars += length ($_);
        $words += scalar(split(/\s+/, $_));
}
printf ("%5d %5d %5d %10s", $lines, $words, $chars, $fileName);
Was it helpful?

Solution

Your program is fine, expect that you need to read from the file handle in the while test. At present you are just reading one line from STDIN and repeatedly checking that it is true - i.e. not zero or undef.

Your code should look like this

use strict;
use warnings;

my ($lines, $chars, $words) = (0, 0, 0);

print "Enter a string to be counted";

while (<STDIN>) {
  ++$lines;
  $chars += length;
  $words += scalar split;
}

printf "%5d %5d %5d\n", $lines, $words, $chars;

Note that I have used just length instead of length $_ as $_ is the default parameter for the length operator. $_ only really comes into its own if you use the defaults.

Similarly, the default parameters to split are split ' ', $_ which is what you want in preference to split /\s+/, $_ because the latter returns a zero-length initial field if there are any leading spaces in $_. The special value of a single literal space ' ' just extracts all the sequences of non-space characters, which is almost always what you want. Anything other than just a single space is converted to a regex pattern as normal.

Finally, I have used ++$lines instead of $lines++. The latter is popular only because of the name of the language C++, and it is less common that the value returned by the expression needs to be the original value of the variable rather than the new one. Much more often the increment is used as a statement on its own, as here, when the returned value is irrelevant. If Perl didn't optimise it out (because the context is void and the return value is unused) the code would be doing unnecessary additional work to save the original value of the variable so that it can be returned after the increment. I also think ++$var looks more like the imperative "increment $var" and improves the readability of the code.

OTHER TIPS

Your input has to be within the loop. Else you are processing the same string over and over again.

Maybe this is what you need?

use strict;
use warnings;

print "Enter a string to be counted:\n";
my $lines = 0;
my $chars = 0;
my $words = 0;
while (<>) {
    chomp;
    $lines++;
    $chars += length ($_);
    $words += scalar(split(/\s+/, $_));
}
printf ("lines: %5d words: %5d chars: %5d\n", $lines, $words, $chars);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top