User defined search of a hash - perl

New to Perl. Using Windows 10 w/ Padre IDE. I have a question that has been stumping me and it's probably very simple yet I have yet to find an answer on the net.
Here is the code that is working up until my search. It reads a list.txt file with names and matching phone numbers so I have two hashes (one is name => number, the second is number =>).
my $search = "";
print "File name to be read in: ";
my $textFile = <>;
open FILE, $textFile;
my %hash;
while (<FILE>)
{
chomp;
my ($key, $val) = split / /;
$hash{$key} .= exists $hash{$key} ? "$val" : $val;
}
close FILE;
open FILE, $textFile;
my %hash2;
while (<FILE>)
{
chomp;
my ($val, $key) = split / /;
$hash2{$key} .= exists $hash2{$key} ? "$val" : $val;
}
close FILE;
print "(N) for name (#) for number search and (.) to exit: ";
$search = <>;
if ($search == "N") #heres where we have the problem
{
print "Enter name: ";
my $person = <>; #user defined search looks for Bob Smith
print $hash{$person}; #prints nothing
}
However, when I make the search non-user defined, it works perfected as in:
if ($search == "N")
{
#print "Enter name: ";
#my $person = <>;
print $hash{"Bob Smith"}; #prints Bob Smith's number
}
For the record, I had $search print to screen to verify that there was no issue whenI entered the name in the first place. When I user-define the search, the hash will not pull up. When the name is specically coded into the hash function, it works every time.
Thank you for helping a stumped Perl Noobie.

You have a trailing newline character. You need to chomp before you compare:
$search = <>;
chomp $search;
Also, you should use the string compare eq:
if ($search eq "N")

Related

how to count the specific word inputted in STDIN inside the text (PERL)

how to count the specific word inputted in STDIN inside the text (PERL)
my output just count all the owrds found inside the text . but i need the specific word i inputted inside the STDIN
open my($file), '<','C:\Users\yukari\Desktop\hi.txt' or die "not exist";
print "Search the word:";
$word = <STDIN>;
print "\n";
while ( my $line = <$file> ) {
chomp($line);
# print $line;
foreach $word (split(' ', $line)) {
$count{$word}++;
}
}
foreach $word (sort keys %count) {
print "$word: $count{$word}\n";
}
I believe you want to get a word from the user and count the number of occurrences of that word in the entire text file.
You can try something like this:
use strict;
use warnings;
open(WRITE,'>','log.txt') or die "Unable to open the file";
my $string = <<END;
foo baz bar
baz bar bar foo
foo foo bar bar baz baz
baz baz baz
END
print WRITE $string;
close WRITE;
open(READ, '<','log.txt') or die "unable to open the file";
my $search = <STDIN>;
chomp $search;
my $count = 0;
while ( my $line = <READ> ) {
chomp($line);
my #words = split(' ',$line);
foreach my $word(#words){
$count++ if($word eq $search);
}
}
close READ;
print "Search string: $search, Count: $count","\n";
You have a problem here. You are using the variable $word for three different things.
You are using it as the word that you are searching for:
$word = <STDIN>;
You are using it to store each word on a line read from your file:
foreach $word (split(' ', $line)) {
And you are using it to contain the keys you are reading from your hash at the end.
foreach $word (sort keys %count) {
In particular, the second use is interfering with the first. When you are reading data from your file, you have no way of knowing what word you are looking for.
If you are looking for a single word, there is no need for a hash to store the counts. I'd write your code like this:
# ALWAYS INCLUDE THESE
use strict;
use warnings;
use feature 'say';
# Renamed your variable, it's a file handle, not a file.
# Also, include $! in error message so we know what really
# went wrong.
open my $fh, '<', 'C:\Users\yukari\Desktop\hi.txt'
or die "Can't open file: $!";
print "Search the word:";
my $search_word = <STDIN>;
print "\n";
# Scalar variable to store the count
my $count;
# Remove the $line variable and use Perl's default variable $_
# instead. This makes the code *far* cleaner.
while ( <$file> ) {
chomp;
# By default, split splits $_ on whitespace
foreach $word (split) {
# Skip words we don't care about
next if $word ne $search_word;
# Increment the counter
$count++;
}
}
say "$search_word appeared $word times";

Perl script - Confusing error

When I run this code, I am purely trying to get all the lines containing the word "that" in them. Sounds easy enough. But when I run it, I get a list of matches that contain the word "that" but only at the end of the line. I don't know why it's coming out like this and I have been going crazy trying to solve it. I am currently getting an output of 268 total matches, and the output I need is only 13. Please advise!
#!/usr/bin/perl -w
#Usage: conc.shift.pl textfile word
open (FH, "$ARGV[0]") || die "cannot open";
#array = (1,2,3,4,5);
$count = 0;
while($line = <FH>) {
chomp $line;
shift #array;
push(#array, $line);
$count++;
if ($line =~ /that/)
{
$output = join(" ",#array);
print "$output \n";
}
}
print "Total matches: $count\n";
Don't you want to increment your $count variable only if the line contains "that", i.e.:
if ($line =~ /that/) {
$count++;
instead of incrementing the counter before checking if $line contains "that", as you have it:
$count++;
if ($line =~ /that/) {
Similarly, I suspect that your push() and join() calls, for stashing a matching line in #array, should also be within the if block, only executed if the line contains "that".
Hope this helps!

cant retrieve values from hash reversal (Perl)

I've initialized a hash with Names and their class ranking as follows
a=>5,b=>2,c=>1,d=>3,e=>5
I've this code so far
my %Ranks = reverse %Class; #As I need to find out who's ranked first
print "\nFirst place goes to.... ", $Ranks{1};
The code only prints out
"First place goes to...."
I want it to print out
First place goes to....c
Could you tell me where' I'm going wrong here?
The class hash prints correctly
but If I try to print the reversed hash using
foreach $t (keys %Ranks) {
print "\n $t $Ranks{$t}"; }
It prints
5
abc23
cab2
ord
If this helps in any way
FULL CODE
#Script to read from the data file and initialize it into a hash
my %Code;
my %Ranks;
#Check whether the file exists
open(fh, "Task1.txt") or die "The File Does Not Exist!\n", $!;
while (my $line = <fh>) {
chomp $line;
my #fields = split /,/, $line;
$Code{$fields[0]} = $fields[1];
$Class{$fields[0]} = $fields[2];
}
close(fh);
#Prints the dataset
print "Code \t Name\n";
foreach $code ( keys %Code) {
print "$code \t $Code{$code}\n";
}
#Find out who comes first
my %Ranks = reverse %Class;
foreach $t (keys %Ranks)
{
print "\n $t $Ranks{$t}";
}
print "\nFirst place goes to.... ", $Ranks{1}, "\n";
When you want to check what your data structures actually contain, use Data::Dumper. use Data::Dumper; local $Data::Dumper::Useqq = 1; print(Dumper(\%Class));. You'll find un-chomped newlines.
You need to use chomp. At present your $fields[2] value has a trailing newline.
Change your file read loop to this
while (my $line = <fh>) {
chomp $line;
my #fields = split /,/, $line;
$Code{$fields[0]} = $fields[1];
$Class{$fields[0]} = $fields[2];
}

prompting multiple questions to user (yes/no & file name input)

I want to ask the user multiple questions. I have two types of questions: Y/N or filename input. I'm not sure how to place this all into a nice if structure. And I'm not sure if I should use 'else' statements too. Could someone help we with this? This is what I have so far:
print "Do you want to import a list (Y/N)?"; # first question yes/no
my $input = <STDIN>;
chomp $input;
if ($input =~ m/^[Y]$/i){ #match Y or y
print "Give the name of the first list file:\n";
my $list1 = <STDIN>;
chomp $list1;
print "Do you want to import another gene list file (Y/N)?";
if ($input =~ m/^[Y]$/i){
print "Give the name of the second list file:\n" # can I use $input or do I need to define another variable?;
$list2 = <STDIN>;
chomp $list2;
print "Do you want to import another gene list file (Y/N)?";
}
}
One word: Abstraction.
The solution you currently chose does not scale well, and contains too much repeated code. We will write a subroutine prompt that hides much of the complexity from us:
sub prompt {
my ($query) = #_; # take a prompt string as argument
local $| = 1; # activate autoflush to immediately show the prompt
print $query;
chomp(my $answer = <STDIN>);
return $answer;
}
And now a promt_yn that asks for confirmation:
sub prompt_yn {
my ($query) = #_;
my $answer = prompt("$query (Y/N): ");
return lc($answer) eq 'y';
}
We can now write your code in a way that actually works:
if (prompt_yn("Do you want to import a list")){
my $list1 = prompt("Give the name of the first list file:\n");
if (prompt_yn("Do you want to import another gene list file")){
my $list2 = prompt("Give the name of the second list file:\n");
# if (prompt_yn("Do you want to import another gene list file")){
# ...
}
}
Oh, so it seems you actually want a while loop:
if (prompt_yn("Do you want to import a list")){
my #list = prompt("Give the name of the first list file:\n");
while (prompt_yn("Do you want to import another gene list file")){
push #list, prompt("Give the name of the next list file:\n");
}
...; # do something with #list
}
The #list is an array. We can append elements via push.
A while ago I end up with following:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
if (&prompt_yn("CONTINUE")){
my #res = split(" ",&prompt("ENTER INPUT")) ;
print Dumper #res;
}
else{
print "EXIT\n";
}
sub prompt_yn{
my ($query) = #_;
$query = $query . " (Y/N): ";
print "$query";
while (<>) {
$_ =~ s/^\s+|\s+$//g;
$_ =~ s/\r|\n//g;
if ($_ =~ /\S/){
if ($_ =~ /^y$|^yes$/i){
# if you want information message about entered value uncomment this
# print "You have entered Y\n";
return 1;
}
elsif ($_ =~ /^n$|^no$/i){
# if you want information message about entered value uncomment this
# print "You have entered N\n";
return 0;
}
else{
# if you want information message about entered value uncomment this
# print "You have entered wrong value try again: \n";
}
}
print "$query";
}
}
sub prompt{
my ($query) = #_;
$query = $query . ": ";
print "$query";
while (<>) {
$_ =~ s/^\s+|\s+$//g;
$_ =~ s/\r|\n//g;
if ($_ =~ /\S/){
return $_;
}
print "$query";
}
}
Compared to previous solutions this handles empty inputs.
You can use Sub Routines.
This helps you visibly and logically keep everything in-line.
for instance
&main();
sub main {
print "Do you want to import a list(Y/N)";
my $input = ;
chomp $input;
if($input =~ m/^[Y]$/i) {
&importfile();
} elsif ($input =~ m/^[N]$/i) {
print "you said no";
} else {
print "Invalid option";
}
}
sub importfile
{
print "file name please ";
my $file = STDIN;
# import and process the file here.....
&main();
}
So you can import at many files this way.

Perl: Searching a file

I am creating a perl script that takes in the a file (example ./prog file)
I need to parse through the file and search for a string. This is what I thought would work, but it does not seem to work. The file is one work per line containing 50 lines
#array = < >;
print "Enter the word you what to match\n";
chomp($match = <STDIN>);
foreach $line (#array){
if($match eq $line){
print "The word is a match";
exit
}
}
You're chomping your user input, but not the lines from the file.
They can't match; one ends with \n the other does not. Getting rid of your chomp should solve the problem. (Or, adding a chomp($line) to your loop).
$match = <STDIN>;
or
foreach $line (#array){
chomp($line);
if($match eq $line){
print "The word is a match";
exit;
}
}
Edit in the hope that the OP notices his mistake from the comments below:
Changing eq to == doesn't "fix" anything; it breaks it. You need to use eq for string comparison. You need to do one of the above to fix your code.
$a = "foo\n";
$b = "bar";
print "yup\n" if ($a == $b);
Output:
yup