title: Regex: how to match any string until whitespace, or until punctuation followed by whitespace?
body: <p>I'm trying to write a regular expression which will find URLs in a plain-text string, so that I can wrap them with anchor tags. I know there are <a href="http://daringfireball.net/2010/07/improved_regex_for_matching_urls" rel="nofollow">expressions already available for this</a>, but I want to create my own, mostly because I want to know how it works.</p>
  
  <p>Since it's not going to break anything if my regex fails, my plan is to write something fairly simple. So far that means: 1) match "www" or "http" at the start of a word 2) keep matching until the word ends.</p>
  
  <p>I can do that, AFAICT. I have this: <code>\b(http|www).?[^\s]+</code></p>
  
  <p>Which works on <code>foo www.example.com bar http://www.example.com</code> etc.</p>
  
  <p>The problem is that if I give it <code>foo www.example.com, http://www.example.com</code> it thinks that the comma is a part of the URL.</p>
  
  <p>So, if I am to use one expression to do this, I need to change "...and stop when you see whitespace" to "...and stop when you see whitespace or a piece of punctuation right before whitespace". <strong>This is what I'm not sure how to do.</strong></p>
  
  <p>At the moment, a solution I'm thinking of running with is just adding another test – matching the URL, and then on the next line moving any sneaky punctuation. This just isn't as elegant.</p>
  
  <p>Note: I am writing this in PHP.</p>
  
  <p><em>Aside: why does replacing <code>\s</code> with <code>\b</code> in the expression above not seem to work?</em></p>
  
  <hr>
  
  <p>ETA:</p>
  
  <p>Thanks everyone!</p>
  
  <p>This is what I eventually ended up with, based on Explosion Pills's advice:</p>
  
  <pre><code>function add_links( $string ) {
      function replace( $arr ) {
          if ( strncmp( "http", $arr[1], 4) == 0 ) {
              return "&lt;a href=$arr[1]&gt;$arr[1]&lt;/a&gt;$arr[2]$arr[3]";
          } else {
              return "&lt;a href=" . "http://" . $arr[1] . "&gt;$arr[1]&lt;/a&gt;$arr[2]$arr[3]";
          }
      }
  return preg_replace_callback( '/\b((?:http|www).+?)((?!\/)[\p{P}]+)?(\s|$)/x', replace, $string );
  }
  </code></pre>
  
  <p>I added a callback so that all of the links would start with http://, and did some fiddling with the way it handles punctuation.</p>
  
  <p>It's probably not the Best way to do things, but it works. I've learned a lot about this in the last little while, but there is still more to learn!</p>