【问题标题】:Perl - Using regex to match input in hash key or valuePerl - 使用正则表达式匹配哈希键或值中的输入
【发布时间】:2016-02-01 18:47:27
【问题描述】:

首先,这是一项家庭作业。我在使用正则表达式时遇到了困难,我被困住了。

这是我到目前为止的代码,我让用户指定一个文件名,如果存在,则填充名称的哈希作为键,电话号码作为值。

#!/usr/bin/perl

use strict;

print "\nEnter Filename: ";
my $file = <STDIN>;
chomp $file;

if(!open(my $fileName, "<", "$file"))
{
    print "Sorry, that file doesn't exist!", "\n";
}
else
{
    my %phoneNums;
    while (my $line=<$fileName>) 
    {
        chomp($line);
        (my $name,my $number) = split /:/, $line;
        $phoneNums{$name} = $number;
    }

    print "Read in the file!", "\n\n";

    print "Enter search: ";
    my $input = <STDIN>;
    chomp $input;

    #HERE IS WHERE I'M LOST
}

print "\n";

这是我坚持的部分:

允许用户输入搜索字符串。 使用与手机相同的样式查找匹配项。任何个人 搜索字符串中的字符可以匹配 键,表示搜索字符串中的“2”可以匹配联系人列表中的“2”、“A”、“B”或“C”。匹配可能出现在联系人姓名或电话号码中。要进行匹配,搜索字符串中的每个字符都必须按顺序出现在联系信息中,但不一定在每个字符旁边 其他。例如,搜索字符串“86”(本质上与搜索字符串“TM”或“NU”相同)将匹配“TOM”但不匹配“MOTHER”。 每个电话键上的字符: 0, 1、 2ABC, 3DEF, 4GHI, 5JKL, 6MNO, 7PQRS, 8TUV, 9WXYZ

我只是被困在如何准确地制作所有这些字符类,非常感谢任何帮助。

【问题讨论】:

  • 你想做什么还不清楚。显示输入值意味着您在哈希或文件中拥有的内容以及您希望如何获得输出,这将更有利于您获得答案并为我们提供正确的解决方案。

标签: regex string perl search data-structures


【解决方案1】:

这是一个几乎程序化的方法,通过使用Hash::MultiValue 作弊:

use Hash::MultiValue; # makes reversing and flattening easier

# build a hash from the phone_keypad array or do it manually!
my @phone_keypad = qw(0 1 2ABC 3DEF 4GHI 5JKL 6MNO 7PQRS 8TUV 9WXYZ);
my %num2let =  map { /(\d{1})(\w{3,4})/; 
               if ($2) { $1 => [ split('',$2) ] } else { 0 => [] , 1 => [] } 
               } @phone_keypad ; 

# Invert the hash using Hash::MultiValue
my $num2let_mv = Hash::MultiValue->from_mixed(\%num2let);
my %let2num = reverse $num2let_mv->flatten ;

# TOM in numbers - 866 in letters
my $letters = "TOM" ;
print join '', $let2num{$_} // $_ for (split('', $letters)), "\n"; 
my $phone_input = "866" ;
print join '', @{$num2let{$_}}," " for (split('', $phone_input)) , "\n";

输出

866
TUV MNO MNO

所以这里"TOM" 将与"UNO" 重叠......我喜欢@Sobrique 的回答:-)

要使用电话键盘输入搜索联系人姓名的数组/列表,我们可以创建一个哈希,其中包含姓名的键和值及其对应的数字,然后将“转换后的”姓名值与输入匹配:

use Hash::MultiValue; # makes reversing and flattening easier

my @contacts = <DATA> ;
chomp @contacts;

# build a hash from the phone_keypad array or do it manually!
my @phone_keypad = qw(0 1 2ABC 3DEF 4GHI 5JKL 6MNO 7PQRS 8TUV 9WXYZ);
my %num2let =  map { /(\d{1})(\w{3,4})/; 
               if ($2) { $1 => [ split('',$2) ] } else { 0 => [] , 1 => [] } 
               } @phone_keypad ; 

# Invert the hash using Hasj::MultiValue
my $num2let_mv = Hash::MultiValue->from_mixed(\%num2let);
my %let2num = reverse $num2let_mv->flatten ;

# create key/value pairs for contact database
my %contacts2nums ;
for $contact (@contacts) {
  $contacts2nums{$contact} = join "",  
    map {  $let2num{$_} } split('', uc $contact);
}

my $phone_input = "866";

for my $contact (keys %contacts2nums) {
 print "The text: \"$contact\" matches the input: \"$phone_input\" \n"
   if $phone_input eq $contacts2nums{$contact};
}

__DATA__
Tom
Mother
TIMTOWDI
DAD
Gordon

输出

The text: "Tom" matches the input: "866"

更有条理的方法是将转换操作包装在一个函数中。


附录

使用真正的键盘,您可能会想出一个简单的算法,该算法对于您想要与键盘上的数字相关联的字母更具确定性。您可以根据按键的次数遍历数组:例如两次按下“2”将等于“B”等。您只需要弄清楚如何/何时移动到具有某种超时/等待值的下一个字符。这样,您将有一个更精确的字符串作为搜索的基础。

【讨论】:

    【解决方案2】:

    解决这个问题的方法是编写一个函数,将你的“事物”简化为它们的公共组件。执行此 IMO 的最佳方法是使用哈希:

    my %num_to_letter = (
        0 => [],
        1 => [],
        2 => [ "A", "B", "C" ],
        3 => [ "D", "E", "F" ],
        4 => [ "G", "H", "I" ],
        5 => [ "J", "K", "L" ],
        ## etc.
    
    );
    
    my %letter_to_num;
    foreach my $key ( keys %num_to_letter ) {
        foreach my $element ( @{$num_to_letter{$key}} ) { 
            $letter_to_num{lc($element)} = lc($key); 
        }
    }
    print Dumper \%letter_to_num;
    

    这会创建一个映射,其中字母或数字映射到它们的原始位置 - 有点像这样:

    $VAR1 = {
              'b' => '2',
              'g' => '4',
              'e' => '3',
              'i' => '4',
              'a' => '2',
              'j' => '5',
    ...
    

    注意 - 您可以手动执行此操作,但我更喜欢从顶部地图生成,因为我认为它看起来更整洁。注意 - 我们使用lc 将所有内容都小写,所以这变得不区分大小写。 fc 可能值得一看 - 这是一个类似的工具,但可以处理国际字符。 (虽然在这个例子中不相关)

    然后,您将搜索和“目标”都“减少”到它们的共同值:

    sub normalise {
        my ( $input ) = @_;
    
        #join with no delimiter. 
        return join ( '', 
                 #look up $_ (each letter) in $letter_to_num
                 #if not present, use // operator to return original value. 
                 #this means we get to turn numbers into letters,
                 #but leave things that are already numbers untouched. 
                 map { $letter_to_num{lc($_)} // $_ } 
                      #split the input line into characters. 
                      split ( //, $input ) 
                );
    }
    
    print normalise ("DAD"),"\n";   ## 323
    

    然后比较一个和另一个:

    my $search            = "DAD";
    my $normalised_search = normalise($search);
    print "Searching for: \"$normalised_search\"\n";
    
    my $number_to_match = '00533932388';
    my $string_to_match = "daddyo";
    
    print "Matches number\n"
        if normalise($number_to_match) =~ m/$normalised_search/;
    print "Matches string\n"
        if normalise($string_to_match) =~ m/$normalised_search/;
    

    【讨论】:

    • ++ 简单而真实的 perl - 我的回答有点太聪明了。为了改善我的反应,我开始考虑一种简单的方法来确定与键盘数字相关的三个或四个字母中的哪一个被选中。我没有马上想出任何东西...... :-)
    猜你喜欢
    • 2011-07-08
    • 1970-01-01
    • 2012-04-13
    • 1970-01-01
    • 2013-01-04
    • 2013-07-05
    • 1970-01-01
    • 2021-12-26
    • 1970-01-01
    相关资源
    最近更新 更多