Wednesday, August 24, 2022

Morse Code Library

Morse code has been on my mind every now and then for the past several years. I don't write as much code as I used to since I left the software development industry, but today I decided it was time to write functions to encode and decode morse.

First, let's define an array to map the encodings for each character.

  1. $morse = array(
  2. " " => "/",
  3. "A" => ".-",
  4. "B" => "-...",
  5. "C" => "-.-.",
  6. "D" => "-..",
  7. "E" => ".",
  8. "F" => "..-.",
  9. "G" => "--.",
  10. "H" => "....",
  11. "I" => "..",
  12. "J" => ".---",
  13. "K" => "-.-",
  14. "L" => ".-..",
  15. "M" => "--",
  16. "N" => "-.",
  17. "O" => "---",
  18. "P" => ".--.",
  19. "Q" => "--.-",
  20. "R" => ".-.",
  21. "S" => "...",
  22. "T" => "-",
  23. "U" => "..-",
  24. "V" => "...-",
  25. "W" => ".--",
  26. "X" => "-..-",
  27. "Y" => "-.--",
  28. "Z" => "--..",
  29. "0" => "-----",
  30. "1" => ".----",
  31. "2" => "..---",
  32. "3" => "...--",
  33. "4" => "....-",
  34. "5" => ".....",
  35. "6" => "-....",
  36. "7" => "--...",
  37. "8" => "---..",
  38. "9" => "----.",
  39. "." => ".-.-.-",
  40. "," => "--..--",
  41. "?" => "..--..",
  42. "'" => ".----.",
  43. "!" => "-.-.--",
  44. "/" => "-..-.",
  45. "(" => "-.--.",
  46. ")" => "-.--.-",
  47. "&" => ".-...",
  48. ":" => "---...",
  49. ";" => "-.-.-.",
  50. "=" => "-...-",
  51. "+" => ".-.-.",
  52. "-" => "-....-",
  53. "_" => "..--.-",
  54. "\"" => ".-..-.",
  55. "$" => "...-..-",
  56. "@" => ".--.-.",
  57. );

This covers almost all possible morse characters, with the exception of accented letters.

Next, we need a function to encode messages.

  1. function morseEncodeMessage($message)
  2. {
  3. global $morse;
  4. $result = "";
  5. for ($i = 0; $i <= strlen($message) - 1; $i++)
  6. {
  7. $character = strtoupper(substr($message, $i, 1));
  8. $result .= (array_key_exists($character, $morse) ? $morse[$character] : "........") . " ";
  9. }
  10. $result = rtrim($result);
  11. return $result;
  12. }
We're looping through the message, one character at a time. Lowercase characters are turned into uppercase characters. We check the global array to see if there's a corresponding key, and if so we get the value associated with that key. If the character can't be found, we convert it to a series of eight dots to indicate that an error has occurred. Each encoded character is separated by a space; an extra trailing space is removed before we return the result. Spaces between words are turned into forward slashes.

Now we need a way of decoding messages.

  1. function morseDecodeMessage($message)
  2. {
  3. global $morse;
  4. $result = "";
  5. $characters = explode(" ", $message);
  6. foreach ($characters as &$character)
  7. {
  8. $result .= in_array($character, $morse, true) ? array_search($character, $morse, true) : "ERROR";
  9. }
  10. unset($character);
  11. return $result;
  12. }
We start by splitting the encoded message into an array, using the spaces between each letter as the separator. Then we loop through the local array and check the global array for each value. If the value is found, we retrieve the key, otherwise we indicate that an error has occurred.

Finally, let's write a unit test to make sure our code is working properly.

  1. function morseTest($message)
  2. {
  3. $testInput = $message;
  4. echo "Input:   " . strtoupper($testInput) . "\n";
  5. $testEncoded = morseEncodeMessage($testInput);
  6. echo "Encoded: " . $testEncoded . "\n";
  7. $testDecoded = morseDecodeMessage($testEncoded);
  8. echo "Decoded: " . $testDecoded . "\n";
  9. echo "Result:  Test " . ($testDecoded == strtoupper($testInput) ? "successful" : "failed") . "!\n";
  10. }
Nothing too complicated. Display the message before encoding, encode the message, display the encoded message, decode the message, display the decoded message, and compare the decoded message with the original.

If we execute the following:
morseTest("Hello world!");

We get this result:
Input:   HELLO WORLD!
Encoded: .... . .-.. .-.. --- / .-- --- .-. .-.. -.. -.-.--
Decoded: HELLO WORLD!
Result:  Test successful!

Saturday, May 26, 2018

Temperature Conversion Revisited

I recently completed another ASP.NET project and learned about the app_code folder, which lets you share code between pages. It's slightly similar to the #include directive in Classic ASP, but a lot more structured. I couldn't find much good documentation on it, so most of what I've learned has been through experimentation.
Any files placed into the app_code folder are automatically available from any page in your web application, so you can write: myNamespace.myClass.myFunction(someVariable) and the magic will happen. Alternatively, you can add Imports myNamespace.myClass in the top of your page and call myFunction without all the prefixing.
Though I'm still not a big fan of ASP.NET, this app_code thing is pretty slick and it's inspired me to write more reusable code. I had previously written a collection of functions for temperature conversion, which I've since grown unhappy with after reading Code Complete 2.0 by Steve McConnell because I named my functions like this: CelsiusToFahrenheit, instead of like this: FahrenheitFromCelsius.
I had been planning to write more conversion functions for various units of measurement, but was never satisfied enough with how it was turning out to make it publically available. Besides learning to appreciate ASP.NET, I'm also learning to appreciate object-oriented programming. This whole namespace/class combination with app_code seems to be appropriately suited for doing unit of measurement conversion.
So here's my plan. I've defined a namespace called UOM, which is the manufacturing industry standard abbreviation for unit of measure. Each type of measurement will be a class: temperature, distance, volume, mass, etc. Each class will accept string input so that you can specify both the numeric quantity and the unit of measure simultaneously. The class will use the specified unit of measure to convert values to a common unit of measure, and be able to return values in any equivalent unit of measure.

VB.NET

  1. Dim inside as Temperature
  2. inside = New Temperature("10 C")
  3. Response.Write(inside.getFahrenheit) ' Displays "50"
And so I proudly announce the release of the first installment in the UOM namespace: UOM.Temperature, which handles Celsius, Delisle, Fahrenheit, Kelvin, Newton, Rankine, Réaumur, and Rømer temperature scales.
Download the VB.NET source code for UOM.Temperature from Snipplr.com

Tuesday, September 20, 2011

Force SSL in VB.NET

Today I'm wrapping up a project to convert an existing web site to use SSL communication only. This required adding HTTPS detection in various places to prevent mixed-content errors. I'm not entirely happy with my solution for that yet, so I'm not going to go into that right now.

What I do want to talk about is actually forcing the user over to HTTPS once your site is SSL-ready. The code I had used for many years on classic ASP sites was not working reliably; it would redirect, but to the home page instead of the page you tried to access.

ASP.NET has beefed up the Request object with some additional information we didn't have back in the old days (Request.IsSecureConnection and Request.Url). We also have a new way of including our function libraries, the App_Code folder.

  1. NameSpace myApplication
  2.     Public Class myLibrary
  3.         Public Shared Sub ForceSSL()
  4.             If Not System.Web.HttpContext.Current.Request.IsSecureConnection Then
  5.                 System.Web.HttpContext.Current.Response.Redirect(System.Web.HttpContext.Current.Request.Url.AbsoluteUri.Replace("http://", "https://"))
  6.             End If
  7.         End Sub
  8.     End Class
  9. End NameSpace


Then from each page on the site I import my custom library and run the subroutine:

  1. Imports myApplication.myLibrary
  2. Sub Page_Load(sender as Object, e as EventArgs)
  3.     ForceSSL()
  4.     'Rest of code goes here...
  5. End Sub


Supposedly there's a way to do this without code by modifying some settings in IIS, but I didn't have any success with that. The particular situation I'm dealing with has some fairly severe cohesion/coupling issues with certain pages being accessed from different subdomains.

Download the VB.NET source code for ForceSSL from Snipplr.com

Tuesday, May 10, 2011

Temperature Conversion Revisited

I recently completed another ASP.NET project and learned about the app_code folder, which lets you share code between pages. It's slightly similar to the #include directive in Classic ASP, but a lot more structured. I couldn't find much good documentation on it, so most of what I've learned has been through experimentation.

Any files placed into the app_code folder are automatically available from any page in your web application, so you can write: myNamespace.myClass.myFunction(someVariable) and the magic will happen. Alternatively, you can add Imports myNamespace.myClass in the top of your page and call myFunction without all the prefixing.

Though I'm still not a big fan of ASP.NET, this app_code thing is pretty slick and it's inspired me to write more reusable code. I had previously written a collection of functions for temperature conversion, which I've since grown unhappy with after reading Code Complete 2.0 by Steve McConnell because I named my functions like this: CelsiusToFahrenheit, instead of like this: FahrenheitFromCelsius.

I had been planning to write more conversion functions for various units of measurement, but was never satisfied enough with how it was turning out to make it publically available. Besides learning to appreciate ASP.NET, I'm also learning to appreciate object-oriented programming. This whole namespace/class combination with app_code seems to be appropriately suited for doing unit of measurement conversion.

So here's my plan. I've defined a namespace called UOM, which is the manufacturing industry standard abbreviation for unit of measure. Each type of measurement will be a class: temperature, distance, volume, mass, etc. Each class will accept string input so that you can specify both the numeric quantity and the unit of measure simultaneously. The class will use the specified unit of measure to convert values to a common unit of measure, and be able to return values in any equivalent unit of measure.

VB.NET

  1. Imports UOM
  2. Dim outside as Temperature
  3. outside = New Temperature("10 C")
  4. Response.Write(outside.getFahrenheit) 'Displays "50"

And so I proudly announce the release of the first installment in the UOM namespace: UOM.Temperature, which handles Celsius, Delisle, Fahrenheit, Kelvin, Newton, Rankine, Réaumur, and Rømer temperature scales.

Download the VB.NET source code for UOM.Temperature from Snipplr.com

Tuesday, September 14, 2010

Scalable Site Structure

If you've been developing web sites for a few years, you've probably developed a lot of sites with a structure similar to the following:

  • /css/
  • /images/
  • /scripts/
  • index.php
  • about.php
  • products.php

One of my colleagues likes to keep the root directory as clean as possible, so his site structures look like this:

  • /content/about.php
  • /content/products.php
  • /common/
  • /images/
  • index.php

Which I find slightly irritating because I prefer to keep my CSS separate from my JavaScript, and the /content/ folder is just taking the mess from one place and moving it to another.

Of course, the Holy Grail of site structures is to have clean URLs like this:

  • /about/
  • /css/
  • /images/
  • /products/
  • /scripts/
  • index.php

If you have a dynamic site, this is easily achieved using mod_rewrite, so /about/ becomes index.php?page=about, but sometimes the client doesn't want to pay for a dynamic site. In that case we use the Poor Man's Clean URLs™ wherein we actually create the /about/ and /products/ folders and put an index.php file in each with the static content. The PHP is then only really used to include a header, footer, navigation, etc.

As a site grows, performance and scalability become a (greater) concern. Popular site speed analysis tools often recommend using a content distribution network. Whether you outsource this or try to do it in-house, it sounds like a lot of work.

I hadn't had the privilege of working on a site that experienced enough traffic to be concerned about scalability until earlier this year. The company was running a national television commercial. They had hired another company to develop a micro-site on a separate server for the campaign, but viewers were being directed to the regular corporate site where a gigantic ad enticed them to visit the micro-site. If you're shaking your head right now, me too. The corporate web server was going down almost daily.

The web statistics package indicated that the most downloaded files were the product videos. I still don't know why they aren't using YouTube or Vimeo for this purpose. On a hunch, I copied the video files to the micro-site's web server and changed all the links. They experienced zero downtime after that.

This experience made me think about how to build scalability into a site from the beginning. Something in the back of my mind twigged a memory of reading something about serving static content from a cookieless domain. Cookies aside, it turns out that serving content from multiple subdomains has certain performance advantages.

Any discussion of clean URLs would not be complete without mentioning www.example.com vs. example.com. Dropping the www seems to be the "cool" thing to do, but my personal feeling is that www.example.com is more descriptive of what you will find there. The mail server is mail.example.com, so the web server should be www.example.com.

So here's the site structure I'm planning to use for future projects:

  • /css/ - css.example.com
  • /images/ - images.example.com
  • /images/logos/ - images.example.com/logos/
  • /images/products/ - images.example.com/products/
  • /js/ - js.example.com
  • /www/ - www.example.com
  • /www/about/ - www.example.com/about/
  • /www/products/ - www.example.com/products/
  • /www/index.php - the actual home page
  • /index.php - file which redirects to www.example.com

The subdomains can be hosted from the primary web server while traffic is small, and migrated to dedicated servers when the traffic increases. The URLs to the images, scripts, and stylesheets don't change, so the PHP files don't need to be updated. Use plenty of folders to keep things organized. You can see I made folders at images.example.com to keep the logos separate from product pictures. If there will be a lot of pictures for each product, I would make folders within the products folder to keep them segregated.

Sunday, September 5, 2010

Roman Numerals, Part 4

Keith Alexander of Albuquerque, New Mexico writes:
“I like your Roman Numeral library. I needed a function to test for Roman numerals, so I wrote this one.
  1. // Check to see if the string is a Roman Numeral
  2. // NOTE: this doesn't check for fractions, overbars, the Bede "N" (zero) etc.
  3. // NOTE: It also doesn't check for a well-formed Roman Numeral.
  4. function is_roman_numeral( $roman )
  5. {
  6.     // Strip every non-word character
  7.     // - A-Z, 0-9, apostrophe and understcore are what's left
  8.     $roman = preg_replace( "/[^A-Z0-9_']/iu", "", $roman );
  9.     // if it contains anything other than MDCLXVI, then it's not a Roman Numeral
  10.     $result = preg_match( "/[^MDCLXVI]/u", $roman );
  11.     if( $result )
  12.     {
  13.         return FALSE;
  14.     }
  15.     return TRUE;
  16. }

Who knows if blogger is going to show it properly. If not, just contact me and I'll send it you in email or something. Anyway, it's something I wrote in 5 minutes. If you want to add it to your library, modified or otherwise, please feel free.”

Thanks for writing in Keith, and sorry for the late response. There are two ways to validate a Roman number, using regular expressions like you did, and converting back to an Arabic number (if the conversion fails, it's not a Roman number).

I'm not sure about using a regular expression to remove non-word characters. My gut tells me that anything containing such characters should fail validation as a Roman number. Also, I would reverse the match and eliminate the if statement by directly returning the result of the match.

PHP

  1. function isRoman($roman)
  2. {
  3.     return preg_match("/[MDCLXVI]/u", $roman);
  4. }

ASP

  1. function isRoman(roman)
  2.     dim regEx
  3.     set regEx = new RegExp
  4.     with regEx
  5.         .IgnoreCase = true
  6.         .Global = true
  7.         .Pattern = "[MDCLXVI]"
  8.     end with
  9.     if regEx.Test(roman) then
  10.         isRoman = true
  11.     else
  12.         isRoman = false
  13.     end if
  14.     set regEx = nothing
  15. end function


Calculating annual salary for employees paid hourly

Last time I explained how to calculate the annual salary for an employee who is paid by the hour. Today I'm going to show you by writing a function for it, and we'll write one for monthly salary too.

  1. function annualSalary(someYear, hourlyPay)
  2.     const hoursPerDay = 8
  3.     annualSalary = 0
  4.     for i = 1 to 12
  5.         annualSalary = annualSalary + WorkingDays(someYear, i) * hoursPerDay * hourlyPay
  6.     next
  7. end function
  8. function monthlySalary(someYear, someMonth, hourlyPay)
  9.     const hoursPerDay = 8
  10.     monthlySalary = 0
  11.     monthlySalary = monthlySalary + WorkingDays(someYear, someMonth) * hoursPerDay * hourlyPay
  12. end function


View ASP implementation on Snipplr

Wednesday, September 1, 2010

Number of working days in a month

I didn't realize it's been over a year since my last post. Most of the code I've written this past year has been too specialized to be considered useful to anyone outside of my present employer, but this week I was presented with a problem with our Intranet application where the annual salary of employees who are paid hourly was not being calculated correctly. I'm working with the application vendor to get this corrected in the next version, and learned some interesting things about how to calculate this correctly.

A common formula to calculate annual salary for hourly employees is hourly pay × 40 hours/week × 52 weeks/year. This is very close to accurate, but 365/366 days per year does not divide evenly by 7 days/week.

A more accurate result can be obtained by multiplying the hourly pay by the number of working hours in a year. One way to calculate this is based on the last day of the year. In a non-leap year, there are 2080 working hours if the last day of the year is a Saturday or Sunday, and 2088 working hours if the last day of the year is a weekday. In the case of a leap year, there are 2080 working hours if the last day of the year is a Sunday, 2088 working hours if the last day of the year is a Saturday or Monday, and 2096 working hours for any other weekday.

At our company, we work 8 hours each day, so we can calculate the number of working days rather than the number of working hours and multiply by 8 hours/day. Since the number of days per month is variable, I decided to calculate working days per month separately and sum them up for working days per year.


  1. Function WorkingDays(someYear, someMonth)
  2.     WorkingDays = 0
  3.     For i = 1 To MonthDays(someYear, someMonth)
  4.         If Weekday(DateSerial(someYear, someMonth, i)) <> 1 And Weekday(DateSerial(someYear, someMonth, i)) <> 7 Then
  5.             WorkingDays = WorkingDays + 1
  6.         End If
  7.     Next
  8. End Function

You can take the result and multiply by 8 hours/day to get the number of working hours in a month, and then multiply by the hourly pay to get the monthly salary. The annual working days can be obtained by calling the function in a for i = 1 to 12...next loop, multiplied by 8 hours/day to get the number of working hours in a year, and then multiplied by the hourly pay to get the annual salary.

We ignore holidays and vacation days since the employee is paid for those as well, though technically vacation pay is accured at 4% so you could subtract vacation days (probably either 10 or 15, depending on number of years of service) and multiply by 1.04 to get an even more accurate amount for annual salary.


View ASP implementation on Snipplr.

Saturday, June 6, 2009

Publishing schedule change

As I mentioned last week, I ran out of code samples that are suitable for publishing. All that remains is code in various states of partial completion. I was trying to avoid this, but have decided I need to change the publishing schedule of this blog from weekly to whenever I have something finished. I have some fairly large projects on the go right now that I need to focus on if I hope to complete them in my own lifetime. I'm not even sure that anyone is ever reading this besides myself as I type it, so I don't expect anyone will be upset by this.

Saturday, May 30, 2009

Dynamic Arrays

I have to shamefully admit... I didn't prepare anything for this week. I'd hate to post nothing, so instead I'll post something which I don't consider completely finished yet, but it's far enough along that it probably works just fine.


Working with arrays in classic ASP is frustrating. I wanted to make it more like working with listbox controls in Visual Basic. Note that I wrote this class before I started learning ASP.NET; I wanted to revamp it to bring it in line with the ArrayList class in VB.NET, but just haven't had time yet. More importantly, I haven't really had reason. All my current projects are in PHP or ASP.NET; I've essentially abandoned classic ASP. But I know there are people out there with classic ASP applications which they can't completely rewrite, so I intend to continue writing classic ASP functions.


Considering the enormous size of this class, there's no way I'm going to attempt posting it here, but it'll be on Snipplr as usual. Count, Item, and Items are implemented as properties. Subroutines and functions include: Remove, RemoveAll, Add, BinarySearch, Exists, Random, Reverse, Sort (calls QuickSort; also available are BubbleSort, CombSort, ExchangeSort, and SelectionSort), Shuffle, Swap, Sum, and Product.


View ASP implementation on Snipplr

Saturday, May 23, 2009

Prime Numbers

Once upon a time I wrote some functions for finding and checking prime numbers. In this library of functions you'll find the following:


  • getPrimes() - finds prime numbers up to a specified limit
  • isPrime() - checks if a number is prime using modular division against odd numbers
  • isPrime2() - checks if a number is prime using modular division against known primes
  • primeCount() - counts the number of primes less than or equal to the specified number
  • isComposite() - the opposite of prime
  • isPrimeSpeedTest() - races isPrime againts isPrime2

In theory, checking against known primes should be faster than checking against all odd numbers, but it turned out to be slower because I was first finding the primes with getPrimes(). If a list of prime numbers was hardcoded in an array it might be faster for smaller numbers, and then the odd number method could be used for larger numbers.


View ASP implementation on Snipplr

Saturday, May 16, 2009

Chemistry Library

This week's code is probably the biggest single release I've ever made. I considered splitting it up into two or three weeks, but that would be too much work. Before we get started, you'll need my proper case function.


In this function library, we have three major things going on: look up chemical element symbols by atomic number, look up chemical element names by atomic number, and figure out the electron configuration of an atom given the atomic number.


For the chemical element names and symbols, I made an array of all the named elements; currently the highest named element is atomic number 111. There are elements with higher atomic numbers that have been discovered, but not yet named. There is also the possibility that more elements may be discovered in the future. To handle both these situations, I wrote code to generate standardized names if the array lookup fails.


The function(s) for figuring out electron configuration are my favorite. I started with an array of all the subshells in the order they get filled. Then I wrote a function that knows how many electrons can fit in each type of subshell. Finally, I wrote the main function which fills the subshells, taking into account known exceptions to the rules. (e.g. palladium)


Saturday, May 9, 2009

Fuel consumption

Last November I was at a Service Canada office and picked up a pamphlet called Fuel Consumption Guide 2007. Although too old to be relevant (unless you're buying a used car), it contained some formulae for calculating fuel consumption which inspired me to write some code.


This library of functions is among the longest yet, so I won't be posting the code directly on the blog, but here's a list of the functions included:


  • lph() - Calculate fuel consumption rating in litres per 100 kilometres.
  • mpg() - Calculate fuel consumption rating in miles per gallon.
  • lph2mpg() - Convert miles per (imperial) gallon to litres per 100 kilometres.
  • mpg2lph() - Convert litres per 100 kilometres to miles per (imperial) gallon.
  • fuelConsumption() - Calculate fuel consumption in litres.
  • CO2emissions() - Calculate carbon dioxide emissions in kilograms.

Saturday, May 2, 2009

Regular Expressions

Over the past year, I've posted a lot of code that made use of regular expressions. When used appropriately, they can be very powerful. But regular expressions in ASP are a little more cumbersome than PHP. Wouldn't it be great if ASP had the simplicity of regular expression functions?


Once again, the source code is too long to post here, so it will only be available on Snipplr. This library of functions includes the following:

  • ereg() - case-sensitive regular expression match
  • eregi() - case-insensitive regular expression match
  • ereg_replace() - case-sensitive regular expression replacement
  • eregi_replace() - case-insensitive regular expression replacement
  • sql_regcase() - make regular expression for case insensitive match

View ASP implementation on Snipplr

Saturday, April 25, 2009

Social Insurance Numbers

Here in Canada, our equivalent of the Social Security Number is called Social Insurance Number. It serves the same purpose and has the same demands for privacy surrounding it.


Once again, I won't be posting the full source code on the blog, only on Snipplr, but I will discuss briefly how it works. It consists of three groups of three digits, and is validated using the Luhn algorithm. I also do a quick regular expression validation to check for numbers which invalidly begin with an eight: ^([1-79]{3})[\-\s]?(\d{3})[\-\s]?(\d{3})$.


Saturday, April 18, 2009

Social Security Numbers

Assuming that you have a legitimate need to capture social security numbers (human resources app?), you may want to validate and format them consistently. The actual code for both languages combined is a little bit too long to post, so I'll just talk about the algorithm.


A social security number is a nine-digit number and can be matched with the following regular expression: ^\d{3}\-?\d{2}\-?\d{4}$. If it can't pass this test, it's not a valid social security number. However, passing this simple test doesn't guarantee validity, so we need to keep checking.



  • No digit group can consist of only zeros, and the first group cannot be 666. We check for these errors with the following regular expression: ((000|666)\-?\d{2}\-?\d{4}|\d{3}\-?00\-?\d{4}|\d{3}\-?\d{2}\-?0000.

  • Numbers from 987-65-4320 to 987-65-4329 are reserved for use in advertisements, and other previously legitimate numbers have been invalidated because of use in advertisments. We check for these errors with the following regular expression: 987\-?65\-?432\d{1}|042\-?10\-?3580|062\-?36\-?0749|078\-?05\-?1120|095\-?07\-?3645|128\-?03\-?6045|135\-?01\-?6629|141\-?18\-?6941|165\-?(16|18|20|22|24)\-?7999|189\-?09\-?2294|212\-?09\-?(7694|9999|219\-?09\-?9999|306\-?30\-?2348|308\-?12\-?5070|468\-?28\-?8779|549\-?24\-?1889)

  • Last but not least, the first three numbers are never higher than 772 (well, not yet; this could change in the future). For this I used a simple string conversion and numeric comparison.


The complete, commented source code is available on Snipplr:


Next week we'll head north of the border to my country and see what the Canadian equivalent of the Social Security Number is, and how we can validate them.

Saturday, April 11, 2009

Homeland Security Advisory System

Back in 2002, a bunch of bureaucrats decided that the United States needed an advisory system so that all federal departments and agencies could communicate what the current threat condition was using the same definitions. Sounds like a good idea to me.


The average citizen can see what the current threat condition is by visiting certain government web sites. The web site for the Department of Homeland Security is a good example.


The Department of Homeland Security also provides a little-known web service which returns the threat condition in XML format so that you can do what you want with it. Let's write a function to retrieve and parse this information down to just the threat condition itself.


ASP



  1. function getThreatLevel()

  2.     dim regEx

  3.     set regEx = new RegExp


  4.     with regEx

  5.         .Pattern = ".*\n.*CONDITION=""(.*)"" />"

  6.         .IgnoreCase = true

  7.         .Global = true

  8.     end with


  9.     dim xmlhttp

  10.     set xmlhttp = Server.CreateObject("Msxml2.ServerXMLHTTP")

  11.     xmlhttp.open "GET", "http://www.dhs.gov/dhspublic/getAdvisoryCondition", "False"

  12.     xmlhttp.send


  13.     getThreatLevel = regEx.replace(xmlhttp.responseText, "$1")


  14.     set xmlhttp = nothing

  15.     set regEx = nothing

  16. end function


As is often the case, the PHP version requires a significantly less amount of code.


PHP



  1. function getThreatLevel()

  2. {

  3.     return eregi_replace('.*CONDITION="(.*)" />', '\1', file_get_contents("http://www.dhs.gov/dhspublic/getAdvisoryCondition"));

  4. }


So now we have a string that contains just the word "ELEVATED" (or whatever the current threat level is at the moment; it's been at elevated for several years at the time of this writing). What can you do with it? Feed it into a switch statement and display an image is one possibility.


Saturday, April 4, 2009

Min/Max

SQL contains a number of aggregate functions that can do things like select the largest or smallest number from a set of values. This could be handy in the programming language itself.


ASP

  1. function max(arrNumbers)
  2.     dim result
  3.     result = arrNumbers(0)
  4.     for each i in arrNumbers
  5.         if i > result then
  6.             result = i
  7.         end if
  8.     next
  9.     max = result
  10. end function
  11. function min(arrNumbers)
  12.     dim result
  13.     result = arrNumbers(0)
  14.     for each i in arrNumbers
  15.         if i < result then
  16.             result = i
  17.         end if
  18.     next
  19.     min = result
  20. end function

View ASP implementation on Snipplr

Saturday, March 28, 2009

getFileType

Last week I showed you how to get the size of a file. This week I'm going to show you how to get the type of a file. This is useful if you want to display an icon or bit of text next to a hyperlink to indicate the file type. Some people like to use this to warn visitors when they link to a PDF file, because PDF readers can take a while to load.


ASP

  1. function getFileType(someFile)
  2.     dim fs
  3.     set fs = Server.CreateObject("Scripting.FileSystemObject")
  4.     getFileType = fs.GetExtensionName(Server.MapPath(someFile))
  5.     set fs = nothing
  6. end function

See you next week!


View ASP implementation on Snipplr

Saturday, March 21, 2009

getFileSize

When you provide a link to a file for the user to download, it's sometimes nice to provide the size of the file so that they know what they're getting themselves into.


ASP

  1. function getFileSize(someFile)
  2.     dim fs
  3.     dim file
  4.     set fs = Server.CreateObject("Scripting.FileSystemObject")
  5.     set file = fs.GetFile(Server.MapPath(someFile))
  6.     getFileSize = FormatFileSize(file.size)
  7.     set file = nothing
  8.     set fs = nothing
  9. end function

The size property returns the size in bytes, which could be a very large number. As you may have guessed, we're going to write a FormatFileSize function to wrap around it.


ASP

  1. function FormatFileSize(size)
  2.     dim units
  3.     dim factor
  4.     units = Array("B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
  5.     factor = log(size) \ 7
  6.     FormatFileSize = Round(size / (1024 ^ factor), 2) & units(factor)
  7. end function

I think it will be a very, very long time before we reach yottabytes, so this function should withstand the test of time. Next week I'll show you how to get the file type and why you might want that.


View ASP implementation on Snipplr