Skip to content

Commit 77caa69

Browse files
committed
Luhn.cs: Add ConvertAlphaNumericToNumeric
... method to convert a string containing alphanumeric characters to a string containing only numeric characters. Resolves: No entry
1 parent b54a9de commit 77caa69

File tree

5 files changed

+164
-1
lines changed

5 files changed

+164
-1
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [Unreleased]
8+
### Added
9+
- Add `ConvertAlphaNumericToNumeric` method to convert a string containing alphanumeric characters to a string containing only numeric characters.
10+
711
## [1.0.1] - 2024-02-19
812
### Fixed
913
- Fixed a bug in `Luhn.ComputeLuhnNumber` and `Luhn.ComputeLuhnCheckDigit` methods that sometimes returned an incorrect result.

README.md

+29
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# LuhnDotNet
22
An C# implementation of the Luhn algorithm.
33

4+
The Luhn algorithm is a checksum formula used to validate identification numbers like credit card numbers. It works by doubling every second digit from the right, summing all the digits, and checking if the total is a multiple of 10. It's widely used and is specified in ISO/IEC 7812-1.
5+
46
# Build & Test Status Of Default Branch
57
<table>
68
<thead>
@@ -210,6 +212,33 @@ namespace Example4
210212
}
211213
```
212214

215+
## Validate ISIN with LuhnDotNet and ConvertAlphaNumericToNumeric
216+
217+
The `LuhnDotNet` library can be used in combination with the `ConvertAlphaNumericToNumeric` method to validate an International Securities Identification Number (ISIN). An ISIN uniquely identifies a security, such as stocks, bonds or derivatives. It is a 12-character alphanumeric code.
218+
219+
The `ConvertAlphaNumericToNumeric` method is used to convert the alphanumeric ISIN to a numeric string, where each letter in the input string is replaced by its decimal ASCII value minus 55. This numeric string can then be validated using the `Luhn.IsValid` method.
220+
221+
Here is an example of how to use these methods to validate an ISIN:
222+
223+
```csharp
224+
using System;
225+
using LuhnDotNet;
226+
227+
namespace Example5
228+
{
229+
public class Program
230+
{
231+
public static void Main(string[] args)
232+
{
233+
string isin = "US0378331005";
234+
bool isValid = Luhn.IsValid(isin.ConvertAlphaNumericToNumeric());
235+
236+
Console.WriteLine($"The ISIN {isin} is valid: {isValid}");
237+
}
238+
}
239+
}
240+
```
241+
213242
# CLI building instructions
214243
For the following instructions, please make sure that you are connected to the internet. If necessary, NuGet will try to restore the [xUnit](https://xunit.net/) packages.
215244
## Using dotnet to build for .NET 6, .NET 7, .NET 8 and .NET FX 4.x

src/Luhn.cs

+63
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ namespace LuhnDotNet
3838
using System.Diagnostics.CodeAnalysis;
3939
using System.Globalization;
4040
#if !NET6_0_OR_GREATER
41+
using System.Text;
4142
using System.Text.RegularExpressions;
4243
#endif
4344

@@ -162,6 +163,68 @@ public static bool IsValid(string number, byte checkDigit)
162163
.SumDigits() == 0;
163164
}
164165

166+
/// <summary>
167+
/// Converts an alphanumeric string to a numeric string.
168+
/// </summary>
169+
/// <param name="alphaNumeric">The alphanumeric string to convert.</param>
170+
/// <returns>A numeric string where each letter in the input string is replaced by its decimal ASCII value
171+
/// minus 55.</returns>
172+
/// <remarks>
173+
/// This method iterates over each character in the input string. If the character is a letter, it is replaced
174+
/// by its decimal ASCII value minus 55. If the character is a digit, it is left unchanged.
175+
/// </remarks>
176+
public static string ConvertAlphaNumericToNumeric(this string alphaNumeric)
177+
#if NET6_0_OR_GREATER
178+
{
179+
Span<char> result = stackalloc char[alphaNumeric.Length * 2];
180+
int index = 0;
181+
182+
foreach (char c in alphaNumeric.ToUpper())
183+
{
184+
if (char.IsLetter(c))
185+
{
186+
string numericValue = (c - 55).ToString();
187+
foreach (char numChar in numericValue)
188+
{
189+
result[index++] = numChar;
190+
}
191+
}
192+
else if (char.IsDigit(c))
193+
{
194+
result[index++] = c;
195+
}
196+
else
197+
{
198+
throw new ArgumentException($"The character '{c}' is not a letter or a digit!", nameof(alphaNumeric));
199+
}
200+
}
201+
202+
return result[..index].ToString();
203+
}
204+
#else
205+
{
206+
var result = new StringBuilder();
207+
208+
foreach (char c in alphaNumeric.ToUpper())
209+
{
210+
if (char.IsLetter(c))
211+
{
212+
result.Append(c - 55);
213+
}
214+
else if (char.IsDigit(c))
215+
{
216+
result.Append(c);
217+
}
218+
else
219+
{
220+
throw new ArgumentException($"The character '{c}' is not a letter or a digit!", nameof(alphaNumeric));
221+
}
222+
}
223+
224+
return result.ToString();
225+
}
226+
#endif
227+
165228
/// <summary>
166229
/// Doubling of every second digit.
167230
/// </summary>

src/LuhnDotNet.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<SignAssembly>True</SignAssembly>
77
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
88
<ImplicitUsings>disable</ImplicitUsings>
9+
<LangVersion>latest</LangVersion>
910
<Nullable>disable</Nullable>
1011
<Authors>Sebastian Walther</Authors>
1112
<PackageId>LuhnDotNet</PackageId>

tests/LuhnTest.cs

+67-1
Original file line numberDiff line numberDiff line change
@@ -243,5 +243,71 @@ public void LuhnCheckDigitValidationExceptionTest(string invalidNumber, byte che
243243
{
244244
Assert.Throws<ArgumentOutOfRangeException>(() => IsValid(invalidNumber, checkDigit));
245245
}
246+
247+
/// <summary>
248+
/// Test data for ConvertAlphaNumericToNumeric method.
249+
/// </summary>
250+
public static IEnumerable<object[]> ConvertAlphaNumericToNumericData =>
251+
new List<object[]>
252+
{
253+
new object[] { "A1B2C3", "101112123" },
254+
new object[] { "Z9Y8X7", "359348337" },
255+
new object[] { "123", "123" },
256+
new object[] { "DE0006069008", "13140006069008" },
257+
new object[] { "ABC", "101112" },
258+
new object[] { "", "" },
259+
};
260+
261+
/// <summary>
262+
/// Tests the ConvertAlphaNumericToNumeric method.
263+
/// </summary>
264+
/// <param name="input">Input string</param>
265+
/// <param name="expected">Expected output</param>
266+
[Theory(DisplayName = "Converts an alphanumeric string to a numeric string")]
267+
[MemberData(nameof(ConvertAlphaNumericToNumericData), MemberType = typeof(LuhnTest))]
268+
public void ConvertAlphaNumericToNumericTest(string input, string expected)
269+
{
270+
Assert.Equal(expected, input.ConvertAlphaNumericToNumeric());
271+
}
272+
273+
/// <summary>
274+
/// Tests the ConvertAlphaNumericToNumeric method with invalid input.
275+
/// </summary>
276+
/// <remarks>
277+
/// This test checks if the ConvertAlphaNumericToNumeric method throws an ArgumentException when it is given an
278+
/// invalid input string that contains non-alphanumeric characters. The test uses the Assert.Throws method from
279+
/// xUnit to check if the expected exception is thrown.
280+
/// </remarks>
281+
[Fact]
282+
public void ConvertAlphaNumericToNumericExceptionTest()
283+
{
284+
Assert.Throws<ArgumentException>(()=> "!@#$%^&*()".ConvertAlphaNumericToNumeric());
285+
}
286+
287+
/// <summary>
288+
/// Test data for IsValid method in combination with ConvertAlphaNumericToNumeric.
289+
/// </summary>
290+
public static IEnumerable<object[]> IsValidWithConvertData =>
291+
new List<object[]>
292+
{
293+
new object[] { "DE0006069008", true },
294+
new object[] { "DE0006069007", false },
295+
new object[] { "DE000BAY0017", true },
296+
new object[] { "DE000BAY0018", false },
297+
new object[] { "AU0000XVGZA3", true },
298+
new object[] { "US0378331005", true },
299+
};
300+
301+
/// <summary>
302+
/// Tests the IsValid method in combination with ConvertAlphaNumericToNumeric.
303+
/// </summary>
304+
/// <param name="input">Input string</param>
305+
/// <param name="expected">Expected output</param>
306+
[Theory(DisplayName = "Validates a numeric string converted from an alphanumeric string")]
307+
[MemberData(nameof(IsValidWithConvertData), MemberType = typeof(LuhnTest))]
308+
public void IsValidWithConvertTest(string input, bool expected)
309+
{
310+
Assert.Equal(expected, IsValid(input.ConvertAlphaNumericToNumeric()));
311+
}
246312
}
247-
}
313+
}

0 commit comments

Comments
 (0)