AV1611 Posted May 30, 2012 Share Posted May 30, 2012 First, hello to those who may remember me from YEARS ago Anyways, after all these years, I still suck at regex, so here's a real world problem I need help with: Query returns something like this: C1,C2-6,C7,C14,C15-43 or: C1,C2-C6,C7 or: CX1,CX2-15 or: R1,R2-5,R7 etc... Anyways, I need to convert to this format: (I will use the last example) R1,R2,R3,R4,R5,R7 I can use array or whatever, I just don't know how to do the regex to do it... And before I get asked for code, here is what I have so far LOL... <?php $str="R1,R2-5,R7"; ?> Quote Link to comment Share on other sites More sharing options...
JAY6390 Posted May 30, 2012 Share Posted May 30, 2012 You aren't going to be solving this using regex, at least not all of it. You're going to need to explode the values using the , then us a regex to match the 2-5, then use a for loop to iterate over the values and add them correctly to the array Quote Link to comment Share on other sites More sharing options...
.josh Posted May 30, 2012 Share Posted May 30, 2012 $string = "C1,C2-6,C7,C14,C15-43"; $string = explode(",",$string); $list = array(); foreach ($string as $val) { preg_match('~([a-z]+)(\d+)(?:-(\d+))?~i',$val,$parts); if ( isset($parts[3]) ) { for ($c=$parts[2]; $c<=$parts[3]; $c++) { $list[] = $parts[1] . $c; } } else { $list[] = $val; } } $string = implode(',',$list); edit: modified regex to match 1 or more letter prefix Quote Link to comment Share on other sites More sharing options...
AV1611 Posted May 30, 2012 Author Share Posted May 30, 2012 You aren't going to be solving this using regex, at least not all of it. You're going to need to explode the values using the , then us a regex to match the 2-5, then use a for loop to iterate over the values and add them correctly to the array I figured I would have to explode the "," then the -... but how do I get the "alpha" character, seeing it may be 1-3 characters long? i.e. R, C, CX, etc...? Maybe something that does "grab the beginning of the string until you encounter a number"? How do I do that? Quote Link to comment Share on other sites More sharing options...
JAY6390 Posted May 30, 2012 Share Posted May 30, 2012 [^\d]+ will match everything before the numbers Quote Link to comment Share on other sites More sharing options...
AV1611 Posted May 30, 2012 Author Share Posted May 30, 2012 Almost works... $str = CX1,CX2-6,CX7 yields: CX1,X2,X3,X4,X5,X6,CX7 Seems to work other than that... Quote Link to comment Share on other sites More sharing options...
.josh Posted May 30, 2012 Share Posted May 30, 2012 if you are referring to the code I posted, look at the edit. I noticed afterwards that the letter prefix could be more than 1 char so i adjusted the regex. Quote Link to comment Share on other sites More sharing options...
AV1611 Posted May 30, 2012 Author Share Posted May 30, 2012 if you are referring to the code I posted, look at the edit. I noticed afterwards that the letter prefix could be more than 1 char so i adjusted the regex. My hero Quote Link to comment Share on other sites More sharing options...
AV1611 Posted May 30, 2012 Author Share Posted May 30, 2012 Oops... one last problem... The data is coming from a VERY old database, and the data syntax is not always the same. The following is possible: $str="CX1,CX2-6,CX7,CX9-CX11"; They did CX2-6, then later CX9-CX11. That seems to be a small issue as well... I am so sorry... Quote Link to comment Share on other sites More sharing options...
.josh Posted May 30, 2012 Share Posted May 30, 2012 geez man, you're killin' me. Are there any other surprises or "gotchas" you need to mention? Quote Link to comment Share on other sites More sharing options...
JAY6390 Posted May 30, 2012 Share Posted May 30, 2012 <?php $str = "R1,R2-5,R7"; $parts = explode(',', $str); $out = array(); $regex = '~^([^\d]+)(\d+)-([^\d]*)(\d+)$~'; foreach($parts as $part) { if(preg_match($regex, $part, $matches)) { $range = range($matches[2], $matches[4]); foreach($range as $number) { $out[] = $matches[1] . $number; } } else { $out[] = $part; } } echo '<pre>' . print_r($out, true) . '</pre>'; ?> That's how I would do it. Should work for your last example too Quote Link to comment Share on other sites More sharing options...
.josh Posted May 30, 2012 Share Posted May 30, 2012 good job Jay, but IMO you unnecessarily complicated it. No need to group that additional ([^\d]*) and use extra matched index. Also that would technically match non-alphanumeric chars. Probably not a concern but may as well be more efficient and explicit since it is assumed that the prefix are alpha only. Also, because I was exceedingly bored, I one-lined it: $string = "CX1,CX2-6,CX7,CX9-CX11"; $string = preg_replace_callback('~([a-z]+)(\d+)(?:-[a-z]*(\d+))?~i',create_function('$m','return (isset($m[3]))?($m[1].implode(\',\'.$m[1],range($m[2],$m[3]))):$m[0];'),$string); echo $string; output: CX1,CX2,CX3,CX4,CX5,CX6,CX7,CX9,CX10,CX11 Quote Link to comment Share on other sites More sharing options...
JAY6390 Posted May 30, 2012 Share Posted May 30, 2012 ha yeah I like to keep my groups just so I know where everything is tbh, you are right though it was unnecessary. Love the one liner! Quote Link to comment Share on other sites More sharing options...
AV1611 Posted May 30, 2012 Author Share Posted May 30, 2012 You guys are awesome... I was trying to get the final answer (please don't laugh too loud... it would have worked... but I didn't get part of it right...) anyways, here's my crappy approach. Remember, I don't know regex, but I can improvise LOLOL $string = "CX1,CX2-6,CX7,CX9-CX11"; preg_match('~([a-z]+)(\d+)(?:-(\d+))?~i',$string,$parts); $prefix = $parts[1]; $string=str_replace(" ","",$string); $string=str_replace($prefix,"",$string); $string = explode(",",$string); $c=count($string); $i=0; while($i < $c){ $s=explode("-",$string[$i]); $ct=count($s); $ci=0; while($ci < $ct){ echo $prefix . $s[$ci] . "<br />\r\n"; $ci++; } $i++; } Quote Link to comment Share on other sites More sharing options...
Philip Posted May 30, 2012 Share Posted May 30, 2012 you unnecessarily complicated it $string = preg_replace_callback('~([a-z]+)(\d+)(?:-[a-z]*(\d+))?~i',create_function('$m','return (isset($m[3]))?($m[1].implode(\',\'.$m[1],range($m[2],$m[3]))):$m[0];'),$string); Quote Link to comment Share on other sites More sharing options...
.josh Posted May 30, 2012 Share Posted May 30, 2012 Okay you got me phil, that one-liner is way more complicated from a readability perspective. But to be fair, I was speaking from a "more steps than necessary" perspective. But anyways.. don't be jealous! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.