http://www.perlmonks.org?node_id=791703

spickles has asked for the wisdom of the Perl Monks concerning the following question:

Monks -

I am trying to perform a regex to detect whether a user entered a space into a word. I read the input from STDIN and then split to an array. I step through the array and look for an entry that is a space. I have tried splitting with //, / /, /''/, /' '/,/\s/,/ */ and none of them appear to work correctly. I'm sure it really is working, it's just something I'm missing. If I print the stored array, I only see the first element, and not the others. I have also run an example I found on the web, and when it prints it shows all of the elements but VERY slowly. The subroutine I'm having a problem with is vlan_name. Is there something maybe going on with buffering? Here's the full code:

#!c:/perl/bin/perl use strict; use warnings; use Net::Ping::External qw(ping); my $mgt_address = ''; my $dhcp_pool = ''; my $dhcp_network = ''; my $dhcp_mask = ''; my $dhcp_dgw = ''; my $wism_slot = ''; my $service_vlan = ''; my $native_vlan = ''; my $slot_list = ''; my @slots; my $current_host = ''; my @ip_hosts; my $vlan_list = ''; my %vlans; my @network; my $apply_status = ''; my $i; my $status; my $MYFILE; my $quit = 'n'; my $vlan_number; my $vlan_name; my @pieces; my @vlan_numbers; my @vlan_names; my @different; our $config_string; vlans(); do_split(); system ("cls"); sub vlan_number { if ($_[0]) { print "Enter the WiSM $_[0] VLAN:\n"; if ($_[0] eq 'native') { chomp ($native_vlan = <>); $vlan_number = $native_vlan; } else { chomp ($service_vlan = <>); $vlan_number = $service_vlan; } } else { print "Enter the VLAN number:\n"; chomp ($vlan_number = <>); } @pieces = split (//,$vlan_number); #foreach (@pieces) { # print "$_ \n"; #} foreach (@pieces) { if ($_ =~ /[a-zA-Z]/) { print "Your VLAN number can only contain digits 0-9.\n" +; vlan_number(); } } if ($_[0] eq 'native') { if (($vlan_number ~~ [1..1001]) || ($vlan_number ~~ [1006..4 +094])) { return $vlan_number; } else { print "VLAN number $vlan_number is invalid.\n"; print "Valid VLANs are 1-1001 and 1006-4094.\n"; vlan_number(); } } if (($vlan_number ~~ [2..1001]) || ($vlan_number ~~ [1006..4094]) +) { return $vlan_number; } else { print "VLAN number $vlan_number is invalid.\n"; print "Valid VLANs are 2-1001 and 1006-4094.\n"; vlan_number(); } } #End vlan_number sub vlan_name { print "Enter the VLAN name (without spaces):\n"; chomp ($vlan_name = <>); print "You entered: $vlan_name\n"; @different = split (/ */,$vlan_name); print join(':', split(/ */, 'hi there')), "\n"; print join(':', split(/ */, $vlan_name)), "\n"; for (my $j=0; $j<scalar(@different); $j++) { print "$different[$j] \n"; if ($different[$j] =~ /[^a-zA-Z0-9_-]/) { print "WARNING:\n"; print "Your VLAN name can only contain letters, numbers +, underscore and hyphen.\n"; print "Do not use special characters [~`!@#$%^&*()+=?\| +[]{};:'\",.<>]\n"; vlan_name(); } if ($different[$j] =~ /\s/) { print "WARNING: Your VLAN name cannot contain spaces.\ +n"; vlan_name(); } else { return; } } } sub vlans { vlan_number("native"); push (@vlan_numbers, $vlan_number); push (@vlan_names, "WiSM_Native"); vlan_number("service"); push (@vlan_numbers, $vlan_number); push (@vlan_names, "WiSM_Service"); print "Setup data VLANs. Enter 'q' when finished.\n\n"; while ($quit ne 'q') { #$array{$key} = $value; vlan_number(); vlan_name(); push (@vlan_numbers, $vlan_number); push (@vlan_names, $vlan_name); print "Add another? 'q' to quit, ENTER to continue...\n\n"; chomp ($quit = <>); } for (my $j = 0; $j<scalar(@vlan_numbers); $j++) { print "$vlan_numbers[$j] => $vlan_names[$j]\n"; } } #End VLANs sub quit_check { #my $key = getc(STDIN); #print "Entered value is $key\n"; chomp ($quit = <>); if (($quit eq '') || ($quit eq 'q')) { return; } else { print "Press ENTER to continue, or 'q' to QUIT.\n"; quit_check(); } } sub do_split { my $str = "I'm not as think as you stoned I am"; # split into individual words on whitespace delimiter # and store in array @words my @words = split (/ /, $str); for (my $j=0; $j<scalar(@words); $j++) { print "$words[$j]\n"; sleep 5; } } __END__

Replies are listed 'Best First'.
Re: Save Split to Array
by NetWallah (Canon) on Aug 27, 2009 at 17:49 UTC
    The split is unnecessary for validating VLAN numbers and names.

    Here is sample validation code for vlan name:

    if ( $vlan_name =~ m|^\w[\w-]*$| ){ # Name complies with : # * At least one char # * Starts with a letter, number or underscore # * Contains only alpha, num, underscore, hyphem }else{ # Complain about non-compliant name }
    Similarly, the vlan number numeric check should be:
    if ($vlan_number =~ m|^\d+$|) { # Compliant - numeric only }else{ # Something Not completely numeric - display error. }

         Potentia vobiscum ! (Si hoc legere scis nimium eruditionis habes)

Re: Save Split to Array
by bv (Friar) on Aug 27, 2009 at 16:39 UTC

    Your problem seems to be understanding split. Any part of your string that matches the regular expression argument will be removed -- only the remaining parts populate the returned array.

    my $string = "This word:ding, is:a sound."; $,='!'; #prints "This!word:ding,!is:a!sound." print split( /\s/, $string ); #prints "This word!ding, is!a sound." print split( /:/, $string ); #prints "Th! word:ding, !:a sound." print split( /is/, $string);
    $,=' ';$\=',';$_=[qw,Just another Perl hacker,];print@$_;
Re: Save Split to Array
by ssandv (Hermit) on Aug 27, 2009 at 16:22 UTC

    First of all, a little less code, but some sample output, if possible. It's a lot easier if we get sample output.

    Anyway, split $stuff,// splits between every character. "" also matches / */, so you can see your problem there. The usual split delimiter is /\s+/, and I don't see that in your code anywhere. I get all the elements out of your do_split routine, although I can't imagine why they put a sleep *inside* the loop.

Re: Save Split to Array
by merlyn (Sage) on Aug 27, 2009 at 20:39 UTC
    my $mgt_address = ''; my $dhcp_pool = ''; my $dhcp_network = ''; my $dhcp_mask = ''; my $dhcp_dgw = ''; my $wism_slot = ''; my $service_vlan = ''; my $native_vlan = ''; my $slot_list = ''; my @slots; my $current_host = ''; my @ip_hosts; my $vlan_list = ''; my %vlans; my @network; my $apply_status = ''; my $i; my $status; my $MYFILE; my $quit = 'n'; my $vlan_number; my $vlan_name; my @pieces; my @vlan_numbers; my @vlan_names; my @different; our $config_string;
    Instant code smell at this point. I don't even want to look further.

    I see a number of variables that looks like they should have been a hash, for example. I also see variables needlessly initialized, out of some ritualistic cargo-cult behavior hoping to scare away warnings by simply working around them.

    -- Randal L. Schwartz, Perl hacker

    The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Re: Save Split to Array
by CountZero (Bishop) on Aug 27, 2009 at 21:13 UTC
    If you want to check if a string contains a space, just use this simple regex:
    $string =~ m/ /;
    or
    $string =~ m/\s/;
    if you want to check for all "space"-like characters (such as tabs, CR, ...)

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: Save Split to Array
by NetWallah (Canon) on Aug 28, 2009 at 05:49 UTC
    spickles :Following up on your chatterbox message - let me just say that this community has absolutely the brightest minds in programming.

    It is not uncommon to feel overwhelmed by flatly stated improvement suggestions (which you interpreted as criticism).

    After 8 years in this community, I continue to be corrected - sometimes even cited for my ignorance. This can be humbling, but it is also educational to the extent that the ego bruise is barely noticeable.

    On the subject of this message thread - as a former Network Admin - I can see where you want to go with this program - I'd STRONGLY recommend placing this information in a database - If you are a windows/office user - start with MS access. Create separate tables for DHCP-scope, VLAN, Subnets, switches+routers (Devices), ports, and endpoints (servers, workstations, Ip phones). You can use perl/DBI/Class::DBI to access/update these, but it is easier to use the database software for data entry. Database constraints can also help with data validation.

         Potentia vobiscum ! (Si hoc legere scis nimium eruditionis habes)

      NetWallah -

      I can handle all kinds of criticism - I realize Perl isn't my strong suit. But when someone responds with 'Instant code smell at this point. I don't even want to look further.' that's just not even necessary. There's two options there:
      (1) Ignore your gut reaction to respond that way and just don't say anything (our moms taught us that)
      (2) Respond with a way to help me understand why I get the 'use of uninitialized value' errors, thus resulting in my 'initialization string' at the opening of my script
      As for the database stuff, I can, and do use that often. I have written another PHP/Perl/MySQL interface that allows my company to process hundreds of lightweight access points to the customer's specifications, configure them, and provide a report. For my current application, it's just outputting a list of commands to be run on a switch that isn't local to me, and the output is going to be transient.

        merlyn's reference to "Code Smell" (Wikipedia) comes from recent (~8 years) software development vocabulary. I do not believe he intended it to be derogatory.

        Regarding your issue:
        Use of uninitialized value $_[0] in string eq at ***** line 63, <> line 2
        This is a result of your call:
        vlan_number();
        on line 118.

        That call does not pass any parameters to "vlan_number():.

        When you request $_[0], at line 62, it asks for the first parameter in @_;
        Since @_ (Parameter list) is empty, you get the warning.

             Potentia vobiscum ! (Si hoc legere scis nimium eruditionis habes)

Re: Save Split to Array
by Grey Fox (Chaplain) on Aug 28, 2009 at 16:05 UTC
    spickles
    A simpler way to do the for loop is to use the foreach.
    my $str = "I'm not as think as you stoned I am"; # split into individual words on whitespace delimiter # and store in array @words my @words = split( /\s+/, $str ); foreach my $word (@words) { print "$word\n"; }
    -- Grey Fox
    "We are grey. We stand between the darkness and the light" B5