Search code examples
perlgetc

Why is my program using Perl's getc function not working properly?


I want to calculate the frequency of occurrence of chars in a message using Perl. For instance, if the char "a" appears 10 times in a message, then the frequency would be 10. To do this, I am reading the message from a FILE one char at a time using the getc function. Here's the snippet I have written. It's very basic, I know. But when I compile, I get an error:

Details:

#!/usr/bin/perl

use strict;
use warnings;

my $input=$ARGV[0];

open(INPUT,"<$input");

while(<INPUT>
{
 my $c=getc(INPUT);
 print $c."\n";
}

close(INPUT);

I get the below error when I try to compile it:

Use of uninitialized value in print at AccessChar.pl line 13, <INPUT> line 1.

I am not able to figure out, what's wrong with this script. Could someone help me out with this?

I have even tried using getc INPUT instead of getc(INPUT). I don't think I need to include any other packages while running this script.


Solution

  • Mixing the file read operator (< ... >) with getc is a bad idea. It's not doing what you think it is.

    Try putting some debug output into the program to see what's going on. I tested the program by running it on itself (./getc getc).

    At the start of the while loop, the <INPUT> reads a line from your file and stores it in $_. You then use getc to read the next character from the file. That will be the first character from the second line of your file (probably a newline character - which is likely to be the only character on that line).

    The next time round the loop, the <INPUT> reads the next line of input. That's the use strict line. The getc reads the next character which is the 'u' from use warnings.

    And so it continues to the end of the file. The <INPUT> reads a line and then the getc reads the first character from the next line.

    That's not what you want at all. If you want to read a character at a time then you just need the getc.

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    my $input = shift;
    
    open(my $file, '<', $input);
    
    while (defined(my $c = getc $file)) {
      print "$c\n";
    }
    

    Another alternative would be to just use < ... > and split each line as you read it.

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    my $input = shift;
    
    open(my $file, '<', $input);
    
    while (<$file>) {
      foreach my $c (split //) {
        print "$c\n";
      }
    }
    

    But mixing the two approached is never going to work.