Although average punched cards had eighty columns, often only seventy-two were used for characters; the remaining eight were ignored by software. Hence arbitrary metadata could be included with each card. Most commonly, it seems that a “sequence number” was used, primarily for the purpose of allowing an out-of-order card deck to be sorted into place.*

A manual for the FAP assembler says the following:

If a BCD source deck is serialized in columns 73–80, sequencing information will be checked, and any card out of sequence will be listed both on- and off-line. If a group of correctly sequenced cards is inserted into a deck out of sequence, only the first card of the group will be listed.

For purposes of sequencing, a blank is not considered to be zero; it is given the octal value 60. A serialized card following a card with all blanks in card columns 73–80 will not be sequence checked.

Presumably the BCD codes are compared, which means that letters would sequence in the correct order (A<⋯<Z) and that 9<A. A consequence is that, because of gaps in the encoding of letters, we have I<-<J and R<*< </<S. It appears to be common to use an alphabetic prefix in sequence numbers, as in this assembler.

Was this the usual method of comparison? (If so, then I suppose that the sequence numbers of adjacent cards could be treated as eight-digit base-64 numbers.)

* But this is not necessarily always the case. Here is the beginning of a COBOL compiler for the Burroughs B5500 (available for download along with several other programs), where the numbers are used for organization beyond keeping the cards in order:

BEGIN                                                                   00000000
% * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 00000100
%        LASCIATE OGNI SPERANZA VOI CH ENTRATE                          00000200
%                                      DANTE                            00000300
% * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 00000400
    DEFINE                                                              00000500
         CURRENTLEVEL =  % LEVEL OF CURRENT COMPILER                    00000600
         0                                                              00000700
         #,                                                             00000800
         LOCALEVEL    =  % LOCAL PATCH LEVEL                            00000900
              0                                                         00001000
         #;                                                             00001100
         0    COMPILIER GLOBALS                                         00001300
         1    GLOBALS FOR PASS 1                                        00001400
              12 READACARD STUFF                                        00001500
              13 SCANNER STUFF                                          00001600
              16 MACRO DISKOUT STUFF                                    00001700
              18 OPERAND STUFF                                          00001800
              19 EXPRESSION STUFF                                       00001900
         3    DATA DIVISION - PASS 1                                    00002100
              31 ITEM ELEMITEM PICTUREPROCESS                           00002200
              32 RECORDDEC                                              00002300
              33 FSE FILE SKELETION                                     00002400
              34 WSE WORKING STORAGE                                    00002500
              35 RSR REPORT WRITER                                      00002600
              39 START OF EXECUTABLES                                   00002700
         4    PROCEDURE DIVISION - PASS 1                               00002800
              40 GENERAL PURPOSE STUFF                                  00002900
              41 DATA MANIPULATION STUFF                                00003000
                        EXAMINEP                                        00003100
                        MOVEP                                           00003200
                        SETP                                            00003300
                        ARITHMETICP                                     00003400
                        COMPUTEP                                        00003500
                        DIVIDEP                                         00003600
              42 PROGRAM CONTROL STUFF                                  00003700
                        LBCHK                                           00003800
                        LABELSCAN                                       00003900
                        CONDITIONALSTMT                                 00004000
                        ALTERP                                          00004100
                        GOP                                             00004200
                        IFP                                             00004300
                        PERFORMP                                        00004400
                        SEARCHP                                         00004500
                        STOPP                                           00004600
              43 SORT STUFF                                             00004700
                        RELEASEP                                        00004800
                        RETURNP                                         00004900
                        SORTP                                           00005000
              44 I-O STUFF                                              00005100
                        ACCEPTP                                         00005200
                        CLOSEP                                          00005300
                        DISPLAYP                                        00005400
                        OPENP                                           00005500
                        READP                                           00005600
                        SEEKP                                           00005700
                        WRITEP                                          00005800
              45 REPORT WRITER                                          00005900
              46 SEPARATELY COMPILED STUFF                              00006000
              47 DIAGNOSTIC STUFF                                       00006100
                        DUMPP                                           00006200
                        MONITORP                                        00006300
              48 DECLARATIVE STUFF                                      00006400
                        USEP                                            00006500
              49 PASS1 PROCEDURE DIVISION DRIVER PROCEDURES             00006600
                        STATEMENT                                       00006700
                        PARAGRAPH                                       00006800
                        SECTION                                         00006900
         6    GLOBALS FOR PASS 2                                        00007000
              61 EMITTERS                                               00007100
              62 ERRORS                                                 00007200
              63 PASSMAC                                                00007300
              64 ARRAYDEC SEGSTART SEGEND                               00007400
              65 GETDATA WRITEDATA                                      00007500
         7    FIRST THREE DIVISIONS - PASS 2                            00007600
         8    PROCEDURE DIVISION - PASS 2                               00007700
              80 GENERAL PURPOSE STUFF                                  00007800
              81 MOVE STUFF                                             00007900
              82 ARITH STUFF                                            00008000
              83 CONTROL STUFF                                          00008100
                        LBCHK                                           00008200
                        EXAMINEP                                        00008300
                        LBLDECK                                         00008400
                        BRANCHGEN                                       00008500
              84 SORT STUFF                                             00008600
                        SRTP                                            00008700
                        RELP                                            00008800
                        RETP                                            00008900
              85 CONTROL STUFF                                          00009000
                        SWCHP                                           00009100
                        ALTRP                                           00009200
                        ALGOP                                           00009300
                        ENDPAR                                          00009400
                        PERFORMP                                        00009500
              86 I-O STUFF                                              00009600
              87 REPORT WRITER                                          00009700
              88 SEPARATELY COMPILED STUFF                              00009800
              89 PASS2 PROCEDURE DIVISION DRIVER                        00009900
                        MACRO                                           00010000
         9    COMPILIER WRAP-UP                                         00010100

I admit that the beginning comment isn't relevant, but it's too good not to include. Starting a 21 000-line COBOL compiler with “Abandon all hope, ye who enter” is hilarious.

  • Incidentally, the superscript * in the first paragraph was produced by writing <sup>*; <sup>*</sup> seems to result in a double superscript. – texdr.aft Apr 26 at 15:29
  • 1
    Not sure what you mean about a double superscript. The way it is now (without a closing </sup> tag) the * isn't superscripted at all. – Alex Hajnal Apr 29 at 10:46
  • @AlexHajnal Ugh, you're right; it was a browser issue. But I'm not going to edit it, since I don't want to needlessly bump the question. – texdr.aft Apr 29 at 13:51

The answer is a clear YES.

What was the question again?

More serious, there is no single answer.

It depends on

  • OS used
  • Toolchain used
  • Method used
  • Application the data were meant for
  • Or if there's a checking at all

Already the question if something is octal or BCD or character does quite differ across applications and platforms.

Then there is the factor of time:

  • Methods developed
  • Applications improved
  • RAM size increased
  • Unimaginable large online storage appeared

... and so on.

To get a meaningful answer, one ned to ask for a specific application (compiler), at a specific time and a specific OS and machine setup. Punch cards itself are 'only' a storage medium, not a handling process.

Having said that, a less satisfying but generic answer would be : Detection and sorting was usually not done in any specific encoding variation, but simply by numeric value, or character value of whatever is punched in relevant columns. What method was used depended on the toolchain used.

Further it may be most important to understand that checking was maybe welcome, but not the main use case for line/card numbers. It was all about the ability to 'insert' cards without the need to really insert them at the right location within a stack. When editing a program, noone was keen on browsing thru the stack and picking out cards and replacing them by hand.

After all, work was don on the paper listing, so for replacing and inserting the line number (from the card) was known, as it was printed as well. To replace a card, a new card with the same number was punched and added to the end of the stack. Similar for inserting - of course this time using a number somewhere between the existing cards, line 00050001 to insert one between card 00050000 and 00060000 (*1).

I might have mentioned it already once or twice, punch cards, their handling and interpretation were the founding elements of the 'IDE' at the time. Punch card programming wasn't the same as today, just with paper. The medium allowed - and asked for - different strategies. Some of them may seem more advanced than they are.

Basic methods of handling:

Overwriting by Consecutive Duplicates

Quite many programs didn't report errors when reading duplicate line numbers in sequence, but used only the last one read. This is an important improvement with in conjunction with usage of classic sorters as tools.

Sorting on by using punch card equipment

Sorting was simply done by using (numeric) sorters. A program stack was fed thru a sorter with the stacks merged afterwards.

A sorter simply discriminates cards by the numeric value of a given column into 10 hoppers. So when running the stack with the lowest digit selected, it returns new stacks, one for each (used) value of 0..9 within that column. Put them on top of each other (high goes first) and run it again for the next higher column. Repeat that for as many columns there are. Done, cards are sorted and can be feed into any program/compiler that wants them in a neat order.

It's important to keep in mind, that when done right, the sequence of cards was not changed beyond dictated by their numbers. This means if a stack had multiple cards with the same number, their order was guaranteed to be preserved (*2). So later added cards were still later in sequence, allowing to use above 'overwriting' of duplicate cards.


With some applications offered automatic sorting when reading. This was usually done by creating a linked list. So cards ended up in read order in RAM but were processed in numbered order.

While the storage was usually done in an intermediate format to save on rare RAM, did such applications tend to need more RAM compared with similar that already did a first processing pass over when reading cards.

Using more advanced OS / utilities with phantastic new hardware

With systems offering advanced (disk) storage methods, like ISAM, an operator could read a card stack and turn it into an ISAM file with the line number as key. When later feed as input by an application/compiler, the cards would appear in the right order.

Improved applications using advanced hardware

Last but not least were compilers that used disk files as intermediate storage. They as well offered to sort lines according to numbers. But now we're already way into the 70s.

*1 - In the early days I had to work with a programmer who always used the number 1, despite 9999 available numbers - and he always started at the top. So between card 0005.0000 and 0006.0000 he didn't insert at 0005.5000, but 0005.1000. This is maybe fine if one wants to insert several cards, but devastating on the long run, 'cause if in next iteration an insert before 0005.1000 was to be made, he used 0005.0100 ... and so on. He quickly did run out of numbers when doing several consecutive changes - so he had to repunch the stack. Not a big deal, as that could be done by a little helper function reading in a stack and punching it out again with new numbers - essentially BASIC's RENUM command for punch cards :)

More annoying, the very same man used that scheme as well for label naming - that was before he got moved to structure macros - So labels with a lot of 1s and 0s were his trademark.

*2 - This is essentially the only case where dropping a punch card stack would really turn into a serious problem of bug insertion.

  • This is exactly what I was looking for, thanks. There isn't a lot of information available on this subject, it seems (no doubt because of the vast number of differing conventions). Also, wouldn't it be "best" to treat duplicate numbers as if they were in sequence, so that additions could be made even if there was no available space? In other words, require that cards are nondecreasing, rather than strictly increasing. That's what seems logical to me, but then again I wasn't around when punched cards were in use. – texdr.aft Apr 26 at 17:15
  • 2
    @texdr.aft Not really - at least not from my experience. A repeated number, no matter if in sequence of out of sequence should always be treated as replacement of a previous one, as that was the way patches were distributed. Then again, there were ISAM implementations that allowed to hold multiple cards with the same key (Attribute DUPKEY=YES), wich were presented to sequential reads in the order entered. so it would be possible - but support was not great, and as programmer I would never have walked that way. It's a way too high risk of screwing a source. – Raffzahn Apr 26 at 17:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.