3

I am writing a vb.net program to generate a three digit serial number I will use in printing a barcode.

The requirements are the counter must count:

001 - 999, A00 - A99, B00 - B99, ..., Z00 - Z99

I cannot use the letters O and I

This code simply increments the value I pass to it by 1. I first check if the value is <=998 and if so return the value in 3 digits. I had to put this in a Try statement because passing the value 'A00' caused an error.

The code is still breaking once I hit Z99.

Problem: If the next serial number = Z90 and the user wants to print 35 barcodes I need to stop the operation before it begins and warn the user there are only 10 avail serial numbers remaining

Also, I am also hoping for advice on how I could have accomplished this in a better manner, any advice would be greatly appreciated

    Public Shared Function NextSerial(ByVal value As String) As String

    Try
        If value <= 998 Then
            value += 1
            Return ZeroPad(value, 3)
        End If

    Catch ex As Exception

    End Try
    Const chars As String = "ABCDEFGHJKLMNPQRSTUVWXYZ"
    Dim threenumber As String = ZeroPad(value, 3) 'ensure value is 3 digits.
    Dim alpha As String = threenumber.Substring(0, 1).ToUpper() ' 1st digit
    Dim beta As String = threenumber.Substring(1, 2) 'remaining two digits
    Dim newNumber As String
    Dim nextletter As String

    If beta = "99" Then
        beta = "00"
        nextletter = chars.Substring((chars.IndexOf(alpha, System.StringComparison.Ordinal) + 1), 1)
        newNumber = nextletter + beta
        Return newNumber

    Else
        beta += 1
        newNumber = alpha + ZeroPad(beta, 2)
        Return newNumber

    End If
End Function

Private Shared Function ZeroPad(ByVal number As String, ByVal toLength As Integer) As String
    ZeroPad = number

    'add the necessary leading zeroes to build it up to the desired length.
    Do Until Len(ZeroPad) >= toLength
        ZeroPad = "0" & ZeroPad
    Loop
End Function
OneFineDay
  • 9,004
  • 3
  • 26
  • 37
Mike
  • 35
  • 1
  • 5
  • I would use a class and implement something like `SerialsRemaining` for something like the check for 10 vs 35, **or** make it smart enough to rollover to B00 if that is allowed – Ňɏssa Pøngjǣrdenlarp Jan 21 '15 at 16:21
  • You can use [`String.PadLeft`](https://msdn.microsoft.com/en-us/library/92h5dc07(v=vs.110).aspx) for your `ZeroPad`-method, also, empty `Catch` are [a bad idea](http://stackoverflow.com/questions/1234343/why-are-empty-catch-blocks-a-bad-idea) in most cases. – Tim Schmelter Jan 21 '15 at 16:21
  • That is not how you use a Try...Catch. I would remove that. You should only use Try...Catch on parts of code where you do not have 100% certainty of the outcome. – LarsTech Jan 21 '15 at 16:23
  • 1
    I suggest you use integer for all your check and calculation and only convert to serial number for display. It'll be a lot easier to know how many serial number are remaining. – the_lotus Jan 21 '15 at 16:46
  • Also, there is already a [pad](https://msdn.microsoft.com/en-us/library/system.string.padright(v=vs.110).aspx) function in the string class. Don't need to make your own with a loop. – the_lotus Jan 21 '15 at 16:49
  • So you do **NOT** want to go from `009` to `00A`, `00B`, `00C`, etc... (skipping `O` and `I`)? – Idle_Mind Jan 21 '15 at 17:10
  • @Idle_Mind : I do not, I must count from 001-999 before using any letters. However when I began this project i was told after 999 the next would be 00A. [original question](http://stackoverflow.com/questions/27404702/vb-net-convert-number-to-alphanumeric-equivalent) – Mike Jan 21 '15 at 17:47
  • 1. retrieve last used serial number from database 2. verify total number of barcodes requested will not exceed limit 3. get next serial number 4. print barcode, loop to 3 until total is printed 5. save last serial to database. I have been working on the assumption that no matter the last serial number, W99 for example, all I had to do was increment by one. Now I am not sure if I should be saving A09 in the database, or its actual value (1009), If I do that, then I need to build a method to convert the number – Mike Jan 21 '15 at 17:49

3 Answers3

2

I think you can do this by assuming your first character is the 'hundreds' and converting to a number and incrementing:

Private Function NextSerial(value As String) As String
    Const chars As String = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ"

    Dim numericValue As Integer = 100 * (chars.IndexOf(value.Substring(0, 1))) + Integer.Parse(value.Substring(1, 2))
    numericValue += 1

    Return chars.Substring(numericValue \ 100, 1) + (numericValue Mod 100).ToString.PadLeft(2, "0")
End Function

You should of course perform some error checking at the start of the function to make sure a valid serial number has been handed into the function. I would also put this function into a class and add functions such as isValid, SerialsRemaining and perhaps a function to retrieve a list of multiple serials.

theduck
  • 2,589
  • 13
  • 17
  • 23
  • This solved my problem perfectly! I am going to have the database only store the integer value and convert it before printing. This will allow me to check that I will not exceed the max value of 3,398 before starting. I am attempting to teach myself programming and I must say I have seen a lot of code relating to manipulating numbers, including converting to different bases, base36 for example. This answer reduced everything to a couple lines. Thank you very much!! – Mike Jan 21 '15 at 18:54
1

I created constant strings that represent every available character in each digit position. I then used indexing to lookup the positions of the current serial number & moved one number forward to get the next serial. This will always provide the next serial until you run out of numbers.

Note: this code can easily be made more compact, but I left it as-is thinking it might be clearer.

Const charString1 As String = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ"
Const charString2 As String = "0123456789"
Const charString3 As String = "0123456789"

Public Function NextSerial(ByVal value As String) As String

    ' ensures the input is three chars long
    Dim threenumber As String = Right("000" & value, 3)

    Dim char1 As String = threenumber.Substring(0, 1)
    Dim char2 As String = threenumber.Substring(1, 1)
    Dim char3 As String = threenumber.Substring(2, 1)
    Dim char1Pos As Integer = charString1.IndexOf(char1)
    Dim char2Pos As Integer = charString2.IndexOf(char2)
    Dim char3Pos As Integer = charString3.IndexOf(char3)

    If char1Pos = -1 Or char2Pos = -1 Or char3Pos = -1 Then Throw New Exception("Invalid serial number format")

    ' move to next serial number
    char3Pos += 1

    If char3Pos > charString3.Length() - 1 Then
        char3Pos = 0
        char2Pos += 1
    End If
    If char2Pos > charString2.Length() - 1 Then
        char2Pos = 0
        char1Pos += 1
    End If
    If char1Pos > charString1.Length() - 1 Then Throw New Exception("Out of serial numbers!")

    Return charString1.Substring(char1Pos, 1) & charString2.Substring(char2Pos, 1) & charString3.Substring(char3Pos, 1)

End Function
laylarenee
  • 3,276
  • 7
  • 32
  • 40
0

I suggest you use integer for all your check and calculation and only convert to serial number for display. It'll be a lot easier to know how many serial number are remaining.

Your serial number is similar to integer except everything over 100 is a letter instead of a number.

Note: It's very important to add error checking, this assumes that all input are valid.

Module Module1

    Sub Main()

        Console.WriteLine(SerialNumber.ConvertSerialNumberToInteger("D22"))
        Console.WriteLine(SerialNumber.ConvertIntegerToSerialNumber(322))
        Console.WriteLine(SerialNumber.GetAvailableSerialNumber("Z90"))

        For Each sn As String In SerialNumber.GetNextSerialNumber("X97", 5)
            Console.WriteLine(sn)
        Next

        Console.ReadLine()

    End Sub

End Module

Class SerialNumber

    Private Const _firstPart As String = "ABCDEFGHJKLMNPQRSTUVWXYZ"

    Public Shared Function ConvertSerialNumberToInteger(ByVal serialNumber As String) As Integer

        Return (_firstPart.IndexOf(serialNumber(0)) * 100) + Integer.Parse(serialNumber.Substring(1, 2))
    End Function

    Public Shared Function ConvertIntegerToSerialNumber(ByVal value As Integer) As String

        Return _firstPart(value \ 100) & (value Mod 100).ToString("00")
    End Function

    Public Shared Function GetAvailableSerialNumber(ByVal serialNumber As String)

        Dim currentPosition As Integer
        Dim lastPosition As Integer

        currentPosition = ConvertSerialNumberToInteger(serialNumber)
        lastPosition = ConvertSerialNumberToInteger("Z99")

        Return lastPosition - currentPosition
    End Function

    Public Shared Function GetNextSerialNumber(ByVal serialNumber As String, ByVal amount As Integer) As List(Of String)

        Dim newSerialNumbers As New List(Of String)
        Dim currentPosition As Integer

        currentPosition = ConvertSerialNumberToInteger(serialNumber)

        For i As Integer = 1 To amount
            newSerialNumbers.Add(ConvertIntegerToSerialNumber(currentPosition + i))
        Next

        Return newSerialNumbers
    End Function

End Class
the_lotus
  • 12,668
  • 3
  • 36
  • 53