title: PHP: trim word OR part of it from begining/end of string
body: <p>I need to trim words from begining and end of string. Problem is, sometimes the words can be abbreviated ie. only first three letters (followed by dot).</p>
  
  <p>I tried hard to find suitable regular expression. Basicaly I need to chatch three or more initial characters up to length of replacement, but I cannot find regular expression, that will match variable length and will keep order of characters.</p>
  
  <p>For example, if I need to trim <code>'insurance'</code> from sentence <code>'insur. companies are rich'</code>, then pattern <code>\^[insurance]{3,9}\</code> comes to my mind, but this pattern will also catch words like <code>'sensace'</code>, because order of characters (and their occurance) inside <code>[]</code> is not important for regexp.</p>
  
  <p>Also, at end of string, I need remove serial-numbers, that are abbreviated from beginig - say <code>'XK-25F14'</code> is sometimes presented as <code>'25F14'</code>. So I decided to go purely with character by character comparison.</p>
  
  <p>Therefore I end with following php function</p>
  
  <pre><code>function trimWords($s, $dirt, $case_insensitive = false, $reverse = true)
  {
      $pos = 0;
      $func = $case_insensitive ? 'strncasecmp' : 'strncmp';
  
      // Get number of initial characters, that match in both strings 
      while ($func($s, $dirt, $pos + 1) === 0)
          $pos++;
  
      // If more than 2 initial characters match, then remove the match   
      if ($pos &gt; 2)
          $s = substr($s, $pos);
  
      // Reverse $s and $dirt so it will trim from the end of string
      $s = strrev($s);        
      if ($reverse)
          return trimWords($s, strrev($dirt), $case_insensitive, false);
  
      // After second run return back-reversed string 
      return trim($s, ' .-');
  }
  </code></pre>
  
  <p>I'm happy with this function, but it has one drawback. It trims only one occurence of word. How to make it trim more occurances, i.e. remove both <code>'insurance '</code> from <code>'Insurance insur. companies'</code>.</p>
  
  <p>And I'm also curious, it realy does not exists such regular expression, that will match variable length and will respect order of characters in pattern?</p>
  
  <h2>Final solution</h2>
  
  <p><strong>Thanks to mrhobo</strong> I have ended with function based on regular expression. This function can be easily improved and shall also be the most efficient for this task.</p>
  
  <p>I have modified my previous function and it is two times quicker than regexp, but it can remove only one word per single run, so to be able to remove word from begin and end, it has to runs itself twice and performance is same as regexp and to remove more than one occurance of word, it has to runs itself multiple times, which will then be more and more slower.</p>
  
  <p>The final function goes like this.</p>
  
  <pre><code>function trimWords($string, $word, $case_insensitive = false, $min_abbrv = 3)
  {
      $exc = substr($word, $min_abbrv);
      $pat = null;
  
      $i = strlen($exc);
      while ($i--)
          $pat = '(?&gt;'.preg_quote($exc[$i], '#').$pat.')?';
  
      $pat = substr($word, 0, $min_abbrv).$pat;
      $pat = '#(?&lt;begin&gt;^)?(?:\W*\b'.$pat.'\b\W*)+(?(begin)|$)#';
      if ($case_insensitive)
          $pat .= 'i';
  
      return preg_replace($pat, '', $string);
  }
  </code></pre>
  
  <p>NOTE: with this function, it does not matter, if abbreviation ends with dot or not, it wipes out any shorter form of word and also removes all nonword characters around the word.</p>
  
  <p>EDIT: I just tried create replace pattern like <code>insu(r|ra|ran|ranc|rance)</code> and function with atomic groups is faster by ~30% and with longer words it could be possibly even more efficient.</p>