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?

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 :-)

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

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!