Selena1992 Posted January 6, 2013 Share Posted January 6, 2013 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 , Selena Link to comment Share on other sites More sharing options...
Psycho Posted January 6, 2013 Share Posted January 6, 2013 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. Link to comment Share on other sites More sharing options...
DavidAM Posted January 6, 2013 Share Posted January 6, 2013 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) Link to comment Share on other sites More sharing options...
us2rn4m2 Posted January 6, 2013 Share Posted January 6, 2013 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 ... Link to comment Share on other sites More sharing options...
DavidAM Posted January 6, 2013 Share Posted January 6, 2013 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. Link to comment Share on other sites More sharing options...
us2rn4m2 Posted January 6, 2013 Share Posted January 6, 2013 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; } Link to comment Share on other sites More sharing options...
Barand Posted January 6, 2013 Share Posted January 6, 2013 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; } Link to comment Share on other sites More sharing options...
us2rn4m2 Posted January 6, 2013 Share Posted January 6, 2013 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')); Link to comment Share on other sites More sharing options...
us2rn4m2 Posted January 6, 2013 Share Posted January 6, 2013 Oops my previous code contains error. I come back ! Link to comment Share on other sites More sharing options...
Barand Posted January 6, 2013 Share Posted January 6, 2013 var_dump(validateLicense('aa-8h-MM')); // --> bool(true) Link to comment Share on other sites More sharing options...
Christian F. Posted January 6, 2013 Share Posted January 6, 2013 (edited) 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 January 6, 2013 by Christian F. Link to comment Share on other sites More sharing options...
us2rn4m2 Posted January 6, 2013 Share Posted January 6, 2013 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; } Link to comment Share on other sites More sharing options...
Barand Posted January 6, 2013 Share Posted January 6, 2013 Seriously, you think it's possible for a newbie to write a complex regex ?! :-\ That's why I gave a non-regex solution Link to comment Share on other sites More sharing options...
kicken Posted January 6, 2013 Share Posted January 6, 2013 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 Link to comment Share on other sites More sharing options...
Christian F. Posted January 6, 2013 Share Posted January 6, 2013 (edited) 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 January 6, 2013 by Christian F. Link to comment Share on other sites More sharing options...
Barand Posted January 6, 2013 Share Posted January 6, 2013 @kicken, He-he. A neat way of getting the mod operator in there, but $pairNum==1 would suffice Link to comment Share on other sites More sharing options...
us2rn4m2 Posted January 7, 2013 Share Posted January 7, 2013 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 ! Link to comment Share on other sites More sharing options...
shlumph Posted January 7, 2013 Share Posted January 7, 2013 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. Link to comment Share on other sites More sharing options...
Christian F. Posted January 7, 2013 Share Posted January 7, 2013 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. Link to comment Share on other sites More sharing options...
Psycho Posted January 7, 2013 Share Posted January 7, 2013 @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. Link to comment Share on other sites More sharing options...
ignace Posted January 7, 2013 Share Posted January 7, 2013 if you have something more better than I, please post it ! Pretty much everyone else did. Link to comment Share on other sites More sharing options...
us2rn4m2 Posted January 7, 2013 Share Posted January 7, 2013 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; } Link to comment Share on other sites More sharing options...
ignace Posted January 8, 2013 Share Posted January 8, 2013 Post your code and after you can judge me ! Yes, because this thread really needs yet another way of doing the same thing. Link to comment Share on other sites More sharing options...
haku Posted January 8, 2013 Share Posted January 8, 2013 I believe Barand has posted the best non-regex solution in his first response. If a regex solution is preferred, then there have been two posts here that do it in a single regex. Link to comment Share on other sites More sharing options...
Christian F. Posted January 8, 2013 Share Posted January 8, 2013 (edited) 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 January 8, 2013 by Christian F. Link to comment Share on other sites More sharing options...
Recommended Posts