The typical system for modern players is simply G, D, C, LW and RW. Sometimes LW and RW are written as L and R instead. Of course, these are not the only positions we need to worry about when discussing historical stats. We also have the point, cover-point and rover. These could be written as P, CP and R respectively. But then, in order to use R for rover, we would not be able to use it for right wing, forcing us to use RW and LW for the wings.
Two-letter codes for the positions are undesirable, because they're not efficient. What if a player split time between cover-point and left wing? Should we write that LW/CP, using five characters to show something that we should be able to do in two? I don't think so.
That's why I settled on a (mostly) numerical code to show the positions.
So LW/CP can now be written 62. The goaltender position is different enough from any of the skaters that it need not conform to the system; in fact goaltender stats will be presented separately from skater stats so that position does not need to be included in the codes.
What are the 8 and 9 for? Well, often in game reports newspapers would list the goaltender, point, cover-point, rover, centre and two wings, but would not specify which side each player played. Code 8 is for that type of player. The 9 is for cases, mostly in the very early years of the game, when the four forwards would be listed only as forwards, not by individual position. Players such as that get a 9 in this system.
The S (skater) code is a new one, added after this system was outlined in late 2011. This one will be very rarely needed, however going back into the 1870s it will sometimes be useful, since in these very early days of organized hockey, often the skaters on a team were listed, without specifying their position even as far as splitting defence and forward.