Wednesday, April 30, 2008

Regular Expressions Are Fun

Miriam and I were debugging a regular expression, and it was an educational experience.

The platform is Java, and our problem is:

  • Input is a long string
  • We want to replace occurrences of "PRE[SEARCHSTRING]POST" with "PREreplacedPOST"
  • "PRE" and "POST" are patterns
  • "[SEARCHSTRING]" is a string that contains a lot of special regex character like "[", "\", and "$".

We found java.util.regex.Pattern.quote to help with the special characters in "[SEARCHSTRING]":

String quotedPattern = Pattern.quote(searchString);

Now, how do we match "PRE" and "POST" without losing them? They disappear if we try:

return input.replace("PRE" + quotedPattern + "POST", "replaced")
;

After some fiddling we came up with:

return input.replaceAll("(PRE)" + quotedPattern + "(POST)", "$1replaced$2");

This matches "PRE" and "POST", but references the captured subgroups with "$1" and "$2" so they don't disappear.

But we then tried the following instead:

return input.replaceAll("(?<=PRE)" + quotedPattern + "(?=POST)", "replaced");

"(?=POST)" is a zero-width positive lookahead, which is an incredibly cool technical name. It matches a pattern that does appear ahead ("positive lookahead") but this pattern won't be regarded when checking what parts of the string matched the regular expression ("zero-width"). "(?<=PRE)" is, similarly, a zero-width positive lookbehind. There are also negative lookaheads and lookbehinds that make sure the pattern doesn't appear.

Is there a more elegant way to do this?

submit to reddit Submit Story to Digg

Wednesday, April 23, 2008

Sorting time spans with jQuery Tablesorter

I've been working a little with Christian Bach's nifty Tablesorter plugin for jQuery, and I'd like to share some cool stuff I've learned.

I wanted to sort a column of time spans formatted like this: "2 days 15:11:06". I wrote a custom parser and ended up with this:

$.tablesorter.addParser({
    id: 'timespan',
    is: function(s) { return false; }, // don't auto detect
    format: function(s) { return s.replace(/\D/g,""); },
    type: 'numeric'
});

And the usage:

$("#mytable").tablesorter({
    headers : {
        2 : 'timespan' // the 3rd column is a time span
    }
});

All it's doing is removing all non-digits and asking for a numeric comparison. It should even work equally well for "0 days 02:30:00" and for "02:30:00", since leading zeros don't affect the sorting :-)

submit to reddit Submit Story to Digg

Monday, April 14, 2008

Dead-simple portable JavaScript logger

window.console = {
  log : function (message) {
    if ($('#logpane').length == 0)
      $('body').append("<ul id='logpane'></ul>");
    $("#logpane").append($("<li></li>").text(message));
  }
};

This requires jQuery, and should work on any browser and on most pages.

It intentionally uses the name 'console.log', so it overrides logging to Firebug's logger.

To use it:

<script type="text/javascript">
console.log("foo");
console.log("bar");
<script>

It's not very pretty - just appends a list of log messages at the bottom:

  • foo
  • bar

submit to reddit Submit Story to Digg

Wednesday, April 9, 2008

Firefox + View Source Goodness

This is incredibly cool! In about:config:

  • Set 'view_source.editor.external' to true
  • Set 'view_source.editor.path' to ... whatever! In my case, '...\gvim.exe'
Thanks Brian!

submit to reddit Submit Story to Digg