Jump to content

Recommended Posts

Dear forum member who reads this,

 

I'm attending a school to become a graphic designer and they thought it was a good idea to give us a php case. My knowledge does not go beyond HTML / CSS and php is completely new to me. I try to educate myself now but i just can't figure out what to do with the following case:

 

CASE 3: Car license

 

A car license plate always consists of 8 characters (XX-XX-XX). Each character is a letter, a digit or

a '-' character. A letter is always next to another letter or next to a '-' character. A digit always stands

next to another digit or next to a '-' character.

Create a function that checks a license plate with the above conditions. the

function returns true or false.

 

Tip 1: Do not panic

Tip 2: Mostly good solutions also get points

Tip 3: Use modulo (%)

 

I understand you are not here to do others homework but I'm a little desperate and hope for some help here.

 

Thanks in advance kiss.gif,

 

Selena

Link to comment
https://forums.phpfreaks.com/topic/272746-car-license-validation-help/
Share on other sites

Well, I'm glad you were honest about this being homework. Many of us that are regular helpers hate it when we see a bunch of people posting the exact same question making it seem they are trying to get a solution to a real problem. We don't like to just give a solution to homework problems as it does nothing to help the person learn.

 

So, when you have a problem such as the above. The best thing, in my opinion, is to first understand what the requirements are and ow you would do that process - without programming, i.e. as a human that was doing that validation manually. So, do you understand what would be valid license plates based upon the above requirements?

 

The best solution I can think of would be the use of a regular expression, which I am sure is way beyond your capabilities. And, I am kinda confused by the tip to use modulo. I can't really think of a solution that would use that. There are a few other solutions I can think of to accomplish this, but not knowing what you do and do not know I'm not sure what to suggest. But, one would be to use explode() to convert the string into an array, then verify the contents of each element of the array. But, I don't want to give too much.

The modulo tip also threw me. I can't think of any way it would be useful.

 

However, if the example "XX-XX-XX" is a fixed format (i.e. it will always be three sets of two characters separated by dashes), then you could explode on the dash and test each pair of characters.

 

But the wording of the problem (as stated above) does not explicitly indicate that the format is fixed, i.e. XXX-XXX- is also valid. But again, you can explode on the dashes and test each set of characters (it's just that the character set size is variable)

Hi,

 

Tip 3: Use modulo (%) is a trap ! Ha Ha !!

I think you need something like that

 

function validateLicense($license) {
// A car license plate always consists of 8 characters
if(strlen($license) !==  {
return false;
}
$l = str_split($license);
// Each character is a letter, a digit or a '-' character
foreach($l as $k => $v) {
if(!preg_match('%[a-zA-Z0-9_-]%', $v)) { // You see the modulo (%)
return false;
}
}
return true;
}

var_dump(validateLicense('AB-12-CD'));

You need to write this condition A digit always stands next to another digit or next to a '-' character. It's your homework ... :)

Modulo is a mathmatical term referring to the remainder when one number is divided by another: 3 modulo 2 = 1. Using the percent-symbol -- the usual operator for modulo -- as the delimiter in a regular exp-ression does not constitue the use of "modulo".

 

That RegEx only verifies that the characters are valid (well, if you take out the underscore). There's no real reason to do the split and use a for loop for that.

if (! preg_match('%^[A-Z0-9-]{8}$%i',$plate)) return false;

 

If you then split on the dash, you can use a pair of checks on each component. One check for letters and one check for the digits. Using the anchors (start and end of string) to be sure.

I'm not a 'regex expert', but this code do the job

function validateLicense($license) { 
 // A car license plate always consists of 8 characters
 $strlen = strlen($license);
 if($strlen !==  {
   return 0;
 }
 $i = 0;
 while($i < $strlen) {
 // Each character is a letter, a digit or a '-' character
 if(preg_match('%[A-Z0-9-]%', $license[$i])) {
   if(isset($license[$i + 1])) {
   // A letter is always next to another letter or next to a '-' character
     if(preg_match('%[A-Z]%', $license[$i])) {
       if(!preg_match('%[A-Z-]%', $license[$i + 1])) {
         return 0;
      }
    }
    // A digit always stands next to another digit or next to a '-' character
    if(preg_match('%[0-9]%', $license[$i])) {
       if(!preg_match('%[0-9-]%', $license[$i + 1])) {
         return 0;
      }
    } 
   }
 } else {
    return 0;
  } 
  $i++;
 }
 return 1;
}

As I understand it there should be 3 pairs separated by hyphens and each pair must be two letters or two numbers So I again put forward the "sans regex" solution

 

function validReg($plate)
{
   $arr = explode('-', $plate);
   if (count($arr) != 3) return false;

   foreach ($arr as $pair) {
    if (strlen($pair) != 2) return false;
    if (!ctype_alpha($pair) && !ctype_digit($pair)) return false;
   }
   return true;
}

For a fixed format XX-XX-XX

 

function validateLicense($plate) {
 $p = explode('-', $plate);

 foreach($p as $k) {
   if(preg_match('%[A-Z]%', $k) && preg_match('%[a-z0-9]%', $k)) return false;
   }
   return strlen($plate) == 8 ? true : false;
 }

var_dump(validateLicense('AE-12-CD'));

A few sub-optimal solutions here, I see. As far as I see, the gurus posted really accurate and valuable information on this solution. When using RegExps for this, there is no need for loops or any other functions. A simple one-liner is all it takes.

 

This is the RegExp needed:

'/^(??:[a-z]{2}|\\d{2})\\-){2}(?:[a-z]{2}|\\d{2})\\z/'

 

To explain what it does:

/      # Start the regular exp<b></b>ression rule
^      # Tie the match to the start of the string.
(?:      # Open a non-capturing sub group
 (?:          # Another non-capturing sub group, which captures:
   [a-z]{2}    # 2 letters
   |        # or...
   \\d{2}        # 2 digits
 )          # (close that group)
 \\-          # Followed by a single hyphen.
){2}      # Repeat that pattern (2 letters or digits followed by a hyphen), 2 times.
(?:[a-z]{2}|\\d{2})    # You should recognize this one now. ;-)
\\z      # Tie the match to the end of the string.
/      # Close the RegExp rule

Reason I use the non-capturing sub-groups around the letters/digits matching, is to contain the "or" operator to only those two options. If not, it would compare everything on the left side against the right side. Meaning you'd get "2 letters (first two), the middle, or two digits (last two)".

The binding of the rule to the start and end of the string (with ^ and \z) is to ensure that the rule matches the entire string. Meaning that there can't be any other content than what's specified by the RegExp.

 

Now you're only missing a typecast, the function itself, and the retuning. ;)

Edited by Christian F.

Selena 1992 say

I'm attending a school to become a graphic designer and they thought it was a good idea to give us a php case. My knowledge does not go beyond HTML / CSS and php is completely new to me. I try to educate myself now but i just can't figure out what to do with the following case

 

Seriously, you think it's possible for a newbie to write a complex regex ?! :-\

 

Like I say I'm not a 'regex expert', but sure for my part it's not too late to learn something new.

 

My last code


function validateLicense($plate) {
 $p = explode('-', $plate);

 foreach($p as $k) {
   if(preg_match('%[A-Za-z]%', $k) && preg_match('%[a-z0-9]%', $k)) {
     return false;
   } 
   if(preg_match('%[A-Z0-9]%', $k)) {
     continue;
   } else {
     return false;
   } 
 }
 return strlen($plate) == 8 ? true : false;
}

Small modification of Barand's:

function validReg($plate)
{
$arr = explode('-', $plate);
if (count($arr) != 3) return false;

foreach ($arr as $pairNum=>$pair) {
	if (strlen($pair) != 2) return false;
	else if (($pairNum%2)==1){
		if (!ctype_alnum($pair)) return false;
	}
	else if (!ctype_alpha($pair) && !ctype_digit($pair)) return false;
}
return true;
}

 

The way I read the instructions, the first and last group have to be either both letter or both digits.  The center group however can be a mix.  This also incorporates the modulus operator to identify the center group.

 

eg, AB-1C-EF

A letter is always next to another letter or next to a '-' character. C-

A digit always stands next to another digit or next to a '-' character. -1

Seriously, you think it's possible for a newbie to write a complex regex ?! :-\

And why I wrote the required RegExp, explaining what every single element of it does.

 

Also, to those who were wondering about the modulus tip: I think the author of the assignment expected the students to loop through the string as an array of characters, and then use the modulus operator to find every third character (to test for dash).

In fact, the wording for the whole assignment makes a lot more sense if you approach it from a C-programmer's perspective. As the above method is the exact method used to validate a string in basic C, and why you'd be worried about the neighbouring positions of characters.

 

Edit: Or Kicken's interpretation. That makes sense too.

Incidentally enough, that makes the RegExp even simpler. :)

Edited by Christian F.

function validateLicense($plate) {
 if(substr_count($plate, '-') !== 2) {
   return false;
 }
 if(strrpos($plate, '-') !== 2 && strrpos($plate, '-') !== 5) {
   return false;
 }
 $p = explode('-', $plate);

 foreach($p as $k) {
   if(!preg_match('%^[A-Z|0-9]+$%', $k)) {
     return false;
   }
   if(preg_match('%[A-Z]%', $k) && preg_match('%[0-9]%', $k)) {
     return false;
   }
 }
 return strlen($plate) == 8 ? true : false;
}

var_dump(validateLicense('AA-12-CD'));

 

if you have something more better than I, please post it !

us2rn4m2: Yes, and I've already posted it. Also, some of your regular expressions does not validate the content properly.

More specifically, this one:

'%^[A-Z|0-9]+$%

 

The pipe (|) does not work as the OR operator inside a character class, but is interpreted as a literal pipe instead. Which means that you just allowed a pipe to be a part of the license plate number.

 

As I stated in my first post, all you need is one RegExp, one instance of preg_match (), and one instance of return. You can also use the (bool) typecast, to be in 100% compliance with the requirement, even though it is strictly not necessary as the PHP engine will typecast the values to true|false when necessary anyway.

@us2rn4m2:

 

Why would you want to create an elaborate process that uses regular expressions only to do simple string validations for which there are built-in PHP functions to do? Either create the more elaborate solution to use only "basic" functions and avoid RegEx completely OR use only a RegEx solution - which only requires a single line. Barand provided the first and Christian provided the second. Your solution is, to be perfectly honest, an attempt at a mishmash of the two which doesn't get any of the benefits (string functions are typically much faster than RegEx and RegEx alone can be done with much, much less code). Plus, as pointed out, it wouldn't work anyway.

 

Although, I will say that I thought Christian's RegEx was a little more complicated than it needed to be. I would have written it out a little more plainly

function validLicense($licenseStr)
{
   return preg_match("#(\d{2}|[a-z]{2})-(\d{2}|[a-z]{2})-(\d{2}|[a-z]{2})#i", $licenseStr);
}

 

. . . but there would be benefits/drawbacks to either his or my solution. So, it's probably more of a preference.

I agree

@shlumph

Make sure you understand regex. If your teacher hasn't covered it in class yet, he's going to ask how you knew to use it, and what it does

 

Thanks

@Christian F

The pipe (|) does not work as the OR operator inside a character class, but is interpreted as a literal pipe instead. Which means that you just allowed a pipe to be a part of the license plate number

 

Post your code and after you can judge me !

@ignace

Pretty much everyone else did

 

(sorry for my English !)

Regex is "magical", but a complex regex it's an advanced concept and it's totally inappropriate for a beginner programmer like Selena to start learning PHP with that sh*t. A beginner need to learn simple and basics concepts, and different kind of way how to manipulate PHP functions !!!

 

function validateLicense($plate) {
$p = explode('-', $plate);

foreach($p as $k) {
 if(!preg_match('%^[A-Z0-9]+$%', $k)) {
  return false;
 }
 if(preg_match('%[A-Z]%', $k) && preg_match('%[0-9]%', $k)) {
  return false;
 }
}
return strlen($plate) == 8 ? true : false;
}

Post your code and after you can judge me !

I did, long before you posted yours. I even pointed it out in my reply to you.

Also, if you can't deal with someone pointing out an error in your code, without having to resort to ascribing (false) motives and strawman arguments, you might need to do some soul searching. For future reference I recommend that you read this article, it'll help you understand how communication on technical help fora works.

 

(sorry for my English !)

Regex is "magical", but a complex regex it's an advanced concept and it's totally inappropriate for a beginner programmer like Selena to start learning PHP with that sh*t. A beginner need to learn simple and basics concepts, and different kind of way how to manipulate PHP functions !!!

So, since RegExp is "totally inappropriate" to give someone who's learning, you decided to provide the OP with not only one regular expression, but three(!). Not giving any indication on what they were supposed to do, and one of them even failing to work properly.

I think you need to reconsider how your actions fits with your words, or rather how they don't.

 

Haku summed it up pretty nicely: There are currently three good solutions to the assignment.

 

If we consider kicken's interpretation of the requirements, then I'd change mine (and Psycho's) RegExp to this:

'/^(?:[a-z]{2}|\\d{2})\\-(?:[a-z\\d]{2})\\-)(?:[a-z]{2}|\\d{2})\\z/'

That makes the inner pair match a character group of letters a-z or a digits 0-9, freely mixed, while the outer pairs must be a matched letter or digit group.

Edited by Christian F.
Guest
This topic is now closed to further replies.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.