user414977
user414977

Reputation: 275

Print alpha-numeric characters using regular expressions

I am new to regular expression.

I need to print all combination 8 characters (alphanumeric) which follow the below pattern.

Upvotes: 1

Views: 479

Answers (2)

ikegami
ikegami

Reputation: 386396

Regular expressions are used to define a syntax. The match and substitute operators check if a string matches a syntax pattern defined using a regex. I am unaware of any tools that use a regex pattern to generate strings.


When you have an arbitrary number of nested loops, you want Algorithm::Loops's NestedLoops. In your case, there's a fixed number of loops, but there are so many that NestedLoops comes in useful anyway.

[I'm assuming you're only interested in the letters and numbers in ASCII.]

Naïve (generates all sequences, and filters out the unwanted ones):

use Algorithm::Loops qw( NestedLoops );

my @syms = ('A'..'Z', 'a'..'z', '0'..'9');

my $iter = NestedLoops([ (\@syms) x 8 ]);

while (my @s = $iter->()) {
   my $s = join('', @s);
   next if $s !~ /^[a-zA-Z][0-9]/;
   next if $s =~ /[a-zA-Z]{4}|[0-9]{3}/;
   say $s;
}

Mostly naïve (generates all sequences except obviously bad ones, and filters out the unwanted ones):

use Algorithm::Loops qw( NestedLoops );

my @letters = ('A'..'Z', 'a'..'z');
my @digits  = ('0'..'9');
my @syms    = (@letters, @digits);

my $iter = NestedLoops([
   \@letters,
   \@digits,
   (\@syms) x 6,
]);

while (my @s = $iter->()) {
   my $s = join('', @s);
   next if $s =~ /[a-zA-Z]{4}|[0-9]{3}/;
   say $s;
}

But that still generates a lot of strings that need to be thrown away. The following only generates the string we want. It does that by generating the patterns that will produce desirable strings (LDLLLDLL, LDLLLDLD, LDLLLDDL, LDLLDLLL, ...), then builds strings from those patterns.

Efficient algorithm (which doesn't mean the implementation is efficient):

use Algorithm::Loops qw( NestedLoops );

my @letters = ('A'..'Z', 'a'..'z');
my @digits  = ('0'..'9');

sub make_pat_gen_iter {
   my $raw_pat_gen_iter = NestedLoops([
      ['L'],
      ['D'],
      (['L','D']) x 6,
   ]);

   return sub {
      while (1) {
         my @pat = $raw_pat_gen_iter->();
         return () if !@pat;

         my $pat = join('', @pat);
         next if $pat =~ /L{4}|D{3}/;

         return @pat;
      }
   };
}

sub make_gen_iter {
   my $pat_gen_iter = make_pat_gen_iter();
   my @pat;

   my $gen_sub_iter;

   return sub {
      return () if !$pat_gen_iter;

      while (1) {
         if (!$gen_sub_iter) {
            @pat = $pat_gen_iter->();
            if (!@pat) {
               $pat_gen_iter = undef;
               return ();
            }

            $gen_sub_iter = NestedLoops([
               map { $_ eq 'L' ? \@letters : \@digits } @pat
            ]);
         }

         my @s = $gen_sub_iter->();
         if (!@s) {
            $gen_sub_iter = undef;
            next;
         }

         return join('', @s);
      }
   };
}

my $gen_iter = make_gen_iter();

while (defined( my $s = $gen_iter->() )) {
   say $s;
}

For fun, the following is the complete result of the pattern iterator:

LDLLLDLL    LDLLLDLD    LDLLLDDL    LDLLDLLL    LDLLDLLD
LDLLDLDL    LDLLDLDD    LDLLDDLL    LDLLDDLD    LDLDLLLD
LDLDLLDL    LDLDLLDD    LDLDLDLL    LDLDLDLD    LDLDLDDL
LDLDDLLL    LDLDDLLD    LDLDDLDL    LDLDDLDD    LDDLLLDL
LDDLLLDD    LDDLLDLL    LDDLLDLD    LDDLLDDL    LDDLDLLL
LDDLDLLD    LDDLDLDL    LDDLDLDD    LDDLDDLL    LDDLDDLD

Upvotes: 1

k.abbey
k.abbey

Reputation: 96

You can check each pattern. Let's name the string $str. Generate $str from "00000000" to "zzzzzzzz" and use following if clause to pick up strings that match the patterns.

if ($str =~ /^[A-Za-z][0-9]/        // pattern 1 and 2
  && $str !~ /[0-9]{3}/             // pattern 3
  && $str !~ /[A-Za-z]{4}/)         // pattern 4

Upvotes: 0

Related Questions