Multiple PAN Masking using SQL or C# - c#

I am looking for help to mask multiple PAN numbers in a string like below example which may contain 2 or more PAN numbers (13 to 19 Digits) using either SQL or C#
6022 1129 5484 2459 1 and 6022 1129 5484 2459 1 and 86674380934233115
I tried using my existing C# code but it only mask 1 or 2 PAN cards and leaves one unmasked
`using Microsoft.SqlServer.Server;
using System.Linq;
using System.Text.RegularExpressions;
using System;
public class SQLExternalFunctions
{
public static readonly int lengthCheckLowerLimit = 13;
public static readonly int lengthCheckUpperLimit = 19;
public static readonly int iterationCount = 14;
[SqlFunction]
public static string Mask(string str)
{
var maskedCreditCard = CheckForMasking(str);
return maskedCreditCard;
}
/// <summary>
/// Check for masking condition
/// </summary>
/// <param name="st"></param>
/// <param name="patternsWithSpecialChar"></param>
/// <param name="patternsWithOutSpecialChar"></param>
/// <returns>Return masked string</returns>
private static string CheckForMasking(string orginalString)
{
try
{
string resultData = string.Empty;
string resultStrng = orginalString;
int lenghtPattern = 27;
bool luhnCheck = false;
string pattern = #"((?![ -%,'-\/,\\])[\d, -%,'-\/,\\,\[,\]]{" + lenghtPattern + #"}(?<![ -%,'-\/,\\]))";
//As the pattern length is decreasing from 27, the iteration is
//increasing for the loop to complete the scanning till 13 digit PAN pattern. So, the iterationCount is 18.
for (int i = 0; i <= iterationCount; i++)
{
MatchCollection matchWithSpecialChar = Regex.Matches(orginalString, pattern.ToString());
foreach (var recordWithAlphaNum in matchWithSpecialChar)
{
//Extracting only the numeric part to check the PAN length and valid PAN.
resultData = Regex.Replace(recordWithAlphaNum.ToString(), #"[^0-9]", string.Empty);
//Checking the PAN length smaller than 13 or greater than 19 or the first digit starts with 0 or 1
if (resultData.Length < lengthCheckLowerLimit || resultData.Length > lengthCheckUpperLimit || resultData.First() <= '1')
{
//if upper condition becomes false then value of luhnCheck becomes true and break out from inner loop
luhnCheck = true;
break;
}
//Checking for the invalid PAN - Luhn Check.
if (!IsPANValid(resultData))
{
//if upper condition becomes false then value of luhnCheck becomes true and break out from inner loop
luhnCheck = true;
break;
}
//Masking the PAN
resultStrng = MaskingPANString(resultData, recordWithAlphaNum.ToString(), orginalString);
orginalString = resultStrng;
resultData = string.Empty;
luhnCheck = true;
}
if (luhnCheck == true)
{
//when value of luhnCheck becomes true then it break out from outer loop
break;
}
lenghtPattern = lenghtPattern - 1;
pattern = #"((?![ -%,'-\/,\\])[\d, -%,'-\/,\\,\[,\]]{" + lenghtPattern + #"}(?<![ -%,'-\/,\\]))";
}
return resultStrng;
}
catch (Exception)
{
}
return null;
}
/// <summary>
/// Masking for PAN String
/// </summary>
/// <param name="resultData"></param>
/// <param name="stringContainSpecialCh"></param>
/// <param name="item"></param>
/// <param name="st"></param>
/// <returns>Masked PAN String</returns>
private static string MaskingPANString(string resultData, string item, string orginalString)
{
try
{
string resultStrng = string.Empty;
string firstDigit = string.Empty;
string lastDigit = string.Empty;
string requiredMask = string.Empty;
string maskedString = string.Empty;
switch (resultData.Length)
{
case 13:
//MaskingPAN is accepting 3 parameters
//string for masking,int firstDigit - first 3 digit not for masking, int numberOfMasking - number of masking.
maskedString = MaskingPAN(item.ToString(), 3, 6);
item = Regex.Escape(item.ToString());
resultStrng = Regex.Replace(orginalString, item.ToString(), maskedString);
break;
case 14:
maskedString = MaskingPAN(item.ToString(), 4, 6);
item = Regex.Escape(item.ToString());
resultStrng = Regex.Replace(orginalString, item.ToString(), maskedString);
break;
case 15:
maskedString = MaskingPAN(item.ToString(), 5, 6);
item = Regex.Escape(item.ToString());
resultStrng = Regex.Replace(orginalString, item.ToString(), maskedString);
break;
case 16:
maskedString = MaskingPAN(item.ToString(), 6, 6);
item = Regex.Escape(item.ToString());
resultStrng = Regex.Replace(orginalString, item.ToString(), maskedString);
break;
case 17:
maskedString = MaskingPAN(item.ToString(), 6, 7);
item = Regex.Escape(item.ToString());
resultStrng = Regex.Replace(orginalString, item.ToString(), maskedString);
break;
case 18:
maskedString = MaskingPAN(item.ToString(), 6, 8);
item = Regex.Escape(item.ToString());
resultStrng = Regex.Replace(orginalString, item.ToString(), maskedString);
break;
case 19:
maskedString = MaskingPAN(item.ToString(), 6, 9);
item = Regex.Escape(item.ToString());
resultStrng = Regex.Replace(orginalString, item.ToString(), maskedString);
break;
default:
break;
}
return resultStrng;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Masking 13 , 14 , 15 , 16, 17 , 18 , and 19 digit PAN
/// </summary>
/// <param name="item"></param>
/// <returns>Masked PAN</returns>
private static string MaskingPAN(string item, int firstDigit, int numberOfMasking)
{
try
{
//Loading the PAN in char array.
char[] itemChar = item.ToCharArray();
int counterChar = 0;
int maskingCounter = 0;
//Iterating through the char array.
for (int i = 0; i < itemChar.Length - 1; i++)
{
//For every PAN(13 , 14 , 15 , 16, 17 , 18 , and 19 digit PAN) there is condition for masking like the first 3 digit and last 4 digit will remain same and mask rest of those.
if (Char.IsNumber(itemChar[i]) && counterChar < firstDigit && maskingCounter == 0)
{
counterChar = counterChar + 1;
}
//Masking the number as per the condition for the respective PAN.
else if (Char.IsNumber(itemChar[i]) && counterChar == firstDigit && maskingCounter < numberOfMasking)
{
maskingCounter = maskingCounter + 1;
itemChar[i] = Convert.ToChar("*");
}
//Breaking from the loop once the all the digit are masked.
else if (maskingCounter == numberOfMasking)
{
break;
}
}
return string.Concat(itemChar);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Check whether Credit Card is valid using LuHn Algorithm
/// </summary>
/// <param name="resultData"></param>
/// <returns>True if given string is valid Credit Card and False in case the string is invalid Credit Card</returns>
private static bool IsPANValid(string resultData)
{
try
{
int sum = 0;
bool alternate = false;
for (int i = resultData.Length - 1; i >= 0; i--)
{
char[] chr = resultData.ToArray();
int n = int.Parse(chr[i].ToString());
if (alternate)
{
n *= 2;
if (n > 9)
{
n = (n % 10) + 1;
}
}
sum += n;
alternate = !alternate;
}
return (sum % 10 == 0);
}
catch (Exception)
{
}
return false;
}
}`

Related

Making formulas in c# to convert dollar amount into denominations

I'm making a program to convert any input amount into denominations of twenties, tens, fives, and ones. Here's what I'm having trouble with:
int twenties = dollar/20;
int tens = twenties/2;
int fives = tens/2;
int ones = fives/5;
Dollar is the input amount, the twenties expression seems to be the only one that comes out right. For example: I input 161 and it comes out as 161 is equal to 8 twenties, 4 tens, 2 fives, and 0 ones. But it should be coming out as 161 is equal to 8 twenties, 0 tens, 0 fives, and 1 ones. I know there is a really simple answer to this but I'm just not getting it, thanks guys.
You need to work with the remainder after removing those denominations. The easiest way to get the remainder is the modulus operator, like this...
int dollar = 161;
int twenties = dollar / 20;
int remainder = dollar % 20;
int tens = remainder / 10;
remainder = remainder % 10;
int fives = remainder / 5;
remainder = remainder % 5;
int ones = remainder;
The above approach does not modify the original amount. By refactoring that into a method, it makes it easier to reuse with different denominations:
public int RemoveDenomination(int denomination, ref int amount)
{
int howMany = amount / denomination;
amount = amount % denomination;
return howMany;
}
...which you could use like this...
int dollar = 161;
int hundreds = RemoveDenomination(100, ref dollar);
int fifties = RemoveDenomination(50, ref dollar);
int twenties = RemoveDenomination(20, ref dollar);
int tens = RemoveDenomination(10, ref dollar);
int fives = RemoveDenomination(5, ref dollar);
int ones = dollar;
This approach does modify the dollar value. So if you don't want to change it, make a copy of it in another variable, and work on the copy.
You have to use the remainder and subtract until the remainder becomes 0;
int amount = 161, temp = 0;
int[] denomination = { 20, 10, 5, 1 }; // you can use enums also for
// readbility
int[] count = new int[denomination.Length];
while (amount > 0)
{
count[temp] = amount / denomination[temp];
amount -= count[temp] * denomination[temp];
temp ++;
}
Another option is to use linq:
int[] denominations = new [] { 20, 10, 5, 1 };
List<int> result =
denominations
.Aggregate(new { Result = new List<int>(), Remainder = 161 }, (a, x) =>
{
a.Result.Add(a.Remainder / x);
return new { a.Result, Remainder = a.Remainder % x };
})
.Result;
That returns a list with the values { 8, 0, 0, 1 }.
Alternatively you could do this:
public static Dictionary<string, int> Denominations(int amount)
{
var denominations = new Dictionary<string, int>();
denominations["twenties"] = amount / 20;
amount = amount % 20;
denominations["tens"] = amount / 10;
amount = amount % 10;
denominations["fives"] = amount / 5;
amount = amount % 5;
denominations["ones"] = amount;
return denominations;
}
This console app illustrates a possible solution:
internal static class Program
{
internal static void Main()
{
var results = GetDenominationValues();
foreach (var result in results)
{
Console.WriteLine($"{result.Key} - {result.Value}");
}
Console.ReadLine();
}
private static Dictionary<int, int> GetDenominationValues()
{
var amount = 161;
var denominations = new[] { 20, 10, 5, 1 };
var results = new Dictionary<int, int>();
foreach (var denomination in denominations)
{
results.Add(denomination, amount / denomination);
amount = amount % denomination;
if (amount == 0) break;
}
return results;
}
}
The key difference is in the line:
amount = amount % denomination;
Which uses the remainder of the previous division in the subsequent calculations.
If the division is greater than zero pass on the remainder (using the modulo operator, %) else pass on the original value to the next division in line. Repeat until no more division left.
Also, divide tens with 10, fives with 5 and ones with 1.
Please use below function that returns dictionary
public static Dictionary<string, int> Denominations(int amount)
{
Dictionary<string, int> denominations = new Dictionary<string, int>() { { "twenties", 0 }, { "tens", 0 }, { "fives", 0 }, { "ones", 0 } };
int twenties, tens, fives, ones;
if (amount >= 20)
{
twenties = amount/20;
denominations["twenties"] = twenties;
amount -= twenties * 20;
}
if (amount >= 10)
{
tens = amount/10;
denominations["tens"] = tens;
amount -= tens * 10;
}
if (amount >= 5)
{
fives = amount/5;
denominations["fives"] = fives;
amount -= fives * 5;
}
if (amount >= 1)
{
ones = amount;
denominations["ones"] = ones;
}
return denominations;
}
You want Code number to text ?
MoneyExt mne = new MoneyExt();
string textamount;
textamount = mne.ConvertNumberToENG(10000);
using System;
public class MoneyExt
{
public string ConvertNumberToENG(int amount)
{
var dollars, cents, temp;
var decimalPlace, count;
string[] place = new string[10];
place[2] = " Thousand ";
place[3] = " Million ";
place[4] = " Billion ";
place[5] = " Trillion ";
// String representation of amount.
amount = amount.Trim();
amount = amount.Replace(",", "");
// Position of decimal place 0 if none.
decimalPlace = amount.IndexOf(".");
// Convert cents and set string amount to dollar amount.
if (decimalPlace > 0)
{
cents = GetTens(ref amount.Substring(decimalPlace + 1).PadRight(2, "0").Substring(0, 2));
amount = amount.Substring(0, decimalPlace).Trim();
}
count = 1;
while (amount != "")
{
temp = GetHundreds(amount.Substring(Math.Max(amount.Length, 3) - 3));
if (temp != "")
dollars = temp + place[count] + dollars;
if (amount.Length > 3)
amount = amount.Substring(0, amount.Length - 3);
else
amount = "";
count = count + 1;
}
switch (dollars)
{
case "One":
{
dollars = "One Bath";
break;
}
default:
{
dollars = dollars + " Bath";
break;
}
}
// Select Case cents
// ' Case ""
// ' cents = " and No Cents"
// Case "One"
// cents = " and One Satang"
// Case Else
// cents = " and " & cents & " Satang"
// End Select
ConvertNumberToENG = dollars + cents;
}
// Converts a number from 100-999 into text
public string GetHundreds(string amount)
{
string Result;
if (!int.Parse(amount) == 0)
{
amount = amount.PadLeft(3, "0");
// Convert the hundreds place.
if (amount.Substring(0, 1) != "0")
Result = GetDigit(ref amount.Substring(0, 1)) + " Hundred ";
// Convert the tens and ones place.
if (amount.Substring(1, 1) != "0")
Result = Result + GetTens(ref amount.Substring(1));
else
Result = Result + GetDigit(ref amount.Substring(2));
GetHundreds = Result;
}
}
// Converts a number from 10 to 99 into text.
private string GetTens(ref string TensText)
{
string Result;
Result = ""; // Null out the temporary function value.
if (TensText.StartsWith("1"))
{
switch (int.Parse(TensText))
{
case 10:
{
Result = "Ten";
break;
}
case 11:
{
Result = "Eleven";
break;
}
case 12:
{
Result = "Twelve";
break;
}
case 13:
{
Result = "Thirteen";
break;
}
case 14:
{
Result = "Fourteen";
break;
}
case 15:
{
Result = "Fifteen";
break;
}
case 16:
{
Result = "Sixteen";
break;
}
case 17:
{
Result = "Seventeen";
break;
}
case 18:
{
Result = "Eighteen";
break;
}
case 19:
{
Result = "Nineteen";
break;
}
default:
{
break;
}
}
}
else
{
switch (int.Parse(TensText.Substring(0, 1)))
{
case 2:
{
Result = "Twenty ";
break;
}
case 3:
{
Result = "Thirty ";
break;
}
case 4:
{
Result = "Forty ";
break;
}
case 5:
{
Result = "Fifty ";
break;
}
case 6:
{
Result = "Sixty ";
break;
}
case 7:
{
Result = "Seventy ";
break;
}
case 8:
{
Result = "Eighty ";
break;
}
case 9:
{
Result = "Ninety ";
break;
}
default:
{
break;
}
}
Result = Result + GetDigit(ref TensText.Substring(1, 1)); // Retrieve ones place.
}
GetTens = Result;
}
// Converts a number from 1 to 9 into text.
private string GetDigit(ref string Digit)
{
switch (int.Parse(Digit))
{
case 1:
{
GetDigit = "One";
break;
}
case 2:
{
GetDigit = "Two";
break;
}
case 3:
{
GetDigit = "Three";
break;
}
case 4:
{
GetDigit = "Four";
break;
}
case 5:
{
GetDigit = "Five";
break;
}
case 6:
{
GetDigit = "Six";
break;
}
case 7:
{
GetDigit = "Seven";
break;
}
case 8:
{
GetDigit = "Eight";
break;
}
case 9:
{
GetDigit = "Nine";
break;
}
default:
{
GetDigit = "";
break;
}
}
}
}

What is the the Correct way of sending DateTime data for SNMPV2 using SNMPSHARPNET?

What is the correct way to send DateTime in UTC to a SNMPV2 event using SNMPSHARPNET ?
http://www.snmpsharpnet.com/
We have been using TimeTicks AsnType but have run into issues of data over 29 and half days.
This is the code for reference :
AsnType newMIBValue = null;
if (!string.IsNullOrEmpty(MIBValueString))
{
switch (DataType)
{
case MIBDataType.DateAndTime:
//newMIBValue = new TimeTicks(MIBValueString);
newMIBValue = ConvertDateTimeToOctetString(MIBValueString);
break;
case MIBDataType.SnmpAdminString:
newMIBValue = new OctetString(MIBValueString);
break;
case MIBDataType.TimeTicks:
newMIBValue = new TimeTicks(MIBValueString);
break;
case MIBDataType.IPAddress:
newMIBValue = new IpAddress(MIBValueString);
break;
case MIBDataType.Integer:
newMIBValue = new Integer32(MIBValueString);
break;
default:
break;
}
}
Use OCTET STRING for datetime stuff. TimeTicks is a non-negative integer which specifies the elapsed time between two events, in units of hundredth of a second.
A colleague of mine helped me put this code. Apparently we have send a byte array of hex values per SNMP specification.
private OctetString ConvertStringToOctetString(DateTime dateTimeValueInUTCDateAndTime)
{
var yearInString = dateTimeValueInUTCDateAndTime.Year.ToString("X");
byte[] bytesArray = ConvertToByteArray(string.Format("{0}{1}{2}{3}{4}{5}{6}",
yearInString,
dateTimeValueInUTCDateAndTime.Month.ToString("X").PadLeft(2, '0'),
dateTimeValueInUTCDateAndTime.Day.ToString("X").PadLeft(2, '0'),
dateTimeValueInUTCDateAndTime.Hour.ToString("X").PadLeft(2, '0'),
dateTimeValueInUTCDateAndTime.Minute.ToString("X").PadLeft(2, '0'),
dateTimeValueInUTCDateAndTime.Second.ToString("X").PadLeft(2, '0'),
"00"
));
return new OctetString(bytesArray);
}
public byte[] ConvertToByteArray(string value)
{
byte[] bytes = null;
if (String.IsNullOrEmpty(value))
bytes = null;
else
{
int string_length = value.Length;
int character_index = (value.StartsWith("0x", StringComparison.Ordinal)) ? 2 : 0; // Does the string define leading HEX indicator '0x'. Adjust starting index accordingly.
int number_of_characters = string_length - character_index;
bool add_leading_zero = false;
if (0 != (number_of_characters % 2))
{
add_leading_zero = true;
number_of_characters += 1; // Leading '0' has been striped from the string presentation.
}
bytes = new byte[number_of_characters / 2]; // Initialize our byte array to hold the converted string.
int write_index = 0;
if (add_leading_zero)
{
bytes[write_index++] = FromCharacterToByte(value[character_index], character_index);
character_index += 1;
}
for (int read_index = character_index; read_index < value.Length; read_index += 2)
{
byte upper = FromCharacterToByte(value[read_index], read_index, 4);
byte lower = FromCharacterToByte(value[read_index + 1], read_index + 1);
bytes[write_index++] = (byte)(upper | lower);
}
}
return bytes;
}
private byte FromCharacterToByte(char character, int index, int shift = 0)
{
byte value = (byte)character;
if (((0x40 < value) && (0x47 > value)) || ((0x60 < value) && (0x67 > value)))
{
if (0x40 == (0x40 & value))
{
if (0x20 == (0x20 & value))
value = (byte)(((value + 0xA) - 0x61) << shift);
else
value = (byte)(((value + 0xA) - 0x41) << shift);
}
}
else if ((0x29 < value) && (0x40 > value))
value = (byte)((value - 0x30) << shift);
else
throw new InvalidOperationException(String.Format("Character '{0}' at index '{1}' is not valid alphanumeric character.", character, index));
return value;
}

Roman numerals to integers

In data sometimes the same product will be named with a roman numeral while other times it will be a digit.
Example Samsung Galaxy SII verses Samsung Galaxy S2
How can the II be converted to the value 2?
I've noticed some really complicated solutions here but this is a really simple problem. I made a solution that avoided the need to hard code the "exceptions" (IV, IX, XL, etc). I used a for loop to look ahead at the next character in the Roman numeral string to see if the number associated with the numeral should be subtracted or added to the total. For simplicity's sake I'm assuming all input is valid.
private static Dictionary<char, int> RomanMap = new Dictionary<char, int>()
{
{'I', 1},
{'V', 5},
{'X', 10},
{'L', 50},
{'C', 100},
{'D', 500},
{'M', 1000}
};
public static int RomanToInteger(string roman)
{
int number = 0;
for (int i = 0; i < roman.Length; i++)
{
if (i + 1 < roman.Length && RomanMap[roman[i]] < RomanMap[roman[i + 1]])
{
number -= RomanMap[roman[i]];
}
else
{
number += RomanMap[roman[i]];
}
}
return number;
}
I initially tried using a foreach on the string which I think was a slightly more readable solution but I ended up adding every single number and subtracting it twice later if it turned out to be one of the exceptions, which I didn't like. I'll post it here anyway for posterity.
public static int RomanToInteger(string roman)
{
int number = 0;
char previousChar = roman[0];
foreach(char currentChar in roman)
{
number += RomanMap[currentChar];
if(RomanMap[previousChar] < RomanMap[currentChar])
{
number -= RomanMap[previousChar] * 2;
}
previousChar = currentChar;
}
return number;
}
This is my solution
public int SimplerConverter(string number)
{
number = number.ToUpper();
var result = 0;
foreach (var letter in number)
{
result += ConvertLetterToNumber(letter);
}
if (number.Contains("IV")|| number.Contains("IX"))
result -= 2;
if (number.Contains("XL")|| number.Contains("XC"))
result -= 20;
if (number.Contains("CD")|| number.Contains("CM"))
result -= 200;
return result;
}
private int ConvertLetterToNumber(char letter)
{
switch (letter)
{
case 'M':
{
return 1000;
}
case 'D':
{
return 500;
}
case 'C':
{
return 100;
}
case 'L':
{
return 50;
}
case 'X':
{
return 10;
}
case 'V':
{
return 5;
}
case 'I':
{
return 1;
}
default:
{
throw new ArgumentException("Ivalid charakter");
}
}
}
A more simple and readable C# implementation that:
maps I to 1, V to 5, X to 10, L to 50, C to 100, D to 500, M to 1000.
uses one single foreach loop (foreach used on purpose, with previous value
hold).
adds the mapped number to the total.
subtracts twice the number added before, if I before V or X, X before L or C, C before D or M (not all chars are allowed here!).
returns 0 (not used in Roman numerals) on empty string, wrong letter
or not allowed char used for subtraction.
remark: it's still not totally complete, we didn't check all possible conditions for a valid input string!
Code:
private static Dictionary<char, int> _romanMap = new Dictionary<char, int>
{
{'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}
};
public static int ConvertRomanToNumber(string text)
{
int totalValue = 0, prevValue = 0;
foreach (var c in text)
{
if (!_romanMap.ContainsKey(c))
return 0;
var crtValue = _romanMap[c];
totalValue += crtValue;
if (prevValue != 0 && prevValue < crtValue)
{
if (prevValue == 1 && (crtValue == 5 || crtValue == 10)
|| prevValue == 10 && (crtValue == 50 || crtValue == 100)
|| prevValue == 100 && (crtValue == 500 || crtValue == 1000))
totalValue -= 2 * prevValue;
else
return 0;
}
prevValue = crtValue;
}
return totalValue;
}
I landed here searching for a small implementation of a Roman Numerals parser but wasn't satisfied by the provided answers in terms of size and elegance. I leave my final, recursive implementation here, to help others searching a small implementation.
Convert Roman Numerals by Recursion
The algorithm is able to handle numerals in irregular subtractive notation (f.e. XIIX).
This implementation may only work with well-formed (strings matching /[mdclxvi]*/i) roman numerals.
The implementation is not optimized for speed.
// returns the value for a roman literal
private static int romanValue(int index)
{
int basefactor = ((index % 2) * 4 + 1); // either 1 or 5...
// ...multiplied with the exponentation of 10, if the literal is `x` or higher
return index > 1 ? (int) (basefactor * System.Math.Pow(10.0, index / 2)) : basefactor;
}
public static int FromRoman(string roman)
{
roman = roman.ToLower();
string literals = "mdclxvi";
int value = 0, index = 0;
foreach (char literal in literals)
{
value = romanValue(literals.Length - literals.IndexOf(literal) - 1);
index = roman.IndexOf(literal);
if (index > -1)
return FromRoman(roman.Substring(index + 1)) + (index > 0 ? value - FromRoman(roman.Substring(0, index)) : value);
}
return 0;
}
Try it using this .Netfiddle: https://dotnetfiddle.net/veaNk3
How does it work?
This algorithm calculates the value of a Roman Numeral by taking the highest value from the Roman Numeral and adding/subtracting recursively the value of the remaining left/right parts of the literal.
ii X iiv # Pick the greatest value in the literal `iixiiv` (symbolized by uppercase)
Then recursively reevaluate and subtract the lefthand-side and add the righthand-side:
(iiv) + x - (ii) # Subtract the lefthand-side, add the righthand-side
(V - (ii)) + x - ((I) + i) # Pick the greatest values, again
(v - ((I) + i)) + x - ((i) + i) # Pick the greatest value of the last numeral compound
Finally the numerals are substituted by their integer values:
(5 - ((1) + 1)) + 10 - ((1) + 1)
(5 - (2)) + 10 - (2)
3 + 10 - 2
= 11
I wrote a simple Roman Numeral Converter just now, but it doesn't do a whole lot of error checking, but it seems to work for everything I could throw at it that is properly formatted.
public class RomanNumber
{
public string Numeral { get; set; }
public int Value { get; set; }
public int Hierarchy { get; set; }
}
public List<RomanNumber> RomanNumbers = new List<RomanNumber>
{
new RomanNumber {Numeral = "M", Value = 1000, Hierarchy = 4},
//{"CM", 900},
new RomanNumber {Numeral = "D", Value = 500, Hierarchy = 4},
//{"CD", 400},
new RomanNumber {Numeral = "C", Value = 100, Hierarchy = 3},
//{"XC", 90},
new RomanNumber {Numeral = "L", Value = 50, Hierarchy = 3},
//{"XL", 40},
new RomanNumber {Numeral = "X", Value = 10, Hierarchy = 2},
//{"IX", 9},
new RomanNumber {Numeral = "V", Value = 5, Hierarchy = 2},
//{"IV", 4},
new RomanNumber {Numeral = "I", Value = 1, Hierarchy = 1}
};
/// <summary>
/// Converts the roman numeral to int, assumption roman numeral is properly formatted.
/// </summary>
/// <param name="romanNumeralString">The roman numeral string.</param>
/// <returns></returns>
private int ConvertRomanNumeralToInt(string romanNumeralString)
{
if (romanNumeralString == null) return int.MinValue;
var total = 0;
for (var i = 0; i < romanNumeralString.Length; i++)
{
// get current value
var current = romanNumeralString[i].ToString();
var curRomanNum = RomanNumbers.First(rn => rn.Numeral.ToUpper() == current.ToUpper());
// last number just add the value and exit
if (i + 1 == romanNumeralString.Length)
{
total += curRomanNum.Value;
break;
}
// check for exceptions IV, IX, XL, XC etc
var next = romanNumeralString[i + 1].ToString();
var nextRomanNum = RomanNumbers.First(rn => rn.Numeral.ToUpper() == next.ToUpper());
// exception found
if (curRomanNum.Hierarchy == (nextRomanNum.Hierarchy - 1))
{
total += nextRomanNum.Value - curRomanNum.Value;
i++;
}
else
{
total += curRomanNum.Value;
}
}
return total;
}
private static int convertRomanToInt(String romanNumeral)
{
Dictionary<Char, Int32> romanMap = new Dictionary<char, int>
{
{'I', 1 },
{'V', 5},
{'X', 10},
{'L', 50},
{'C', 100},
{'D', 500},
{'M', 1000}
};
Int32 result = 0;
for (Int32 index = romanNumeral.Length - 1, last = 0; index >= 0; index--)
{
Int32 current = romanMap[romanNumeral[index]];
result += (current < last ? -current : current);
last = current;
}
return result;
}
With Humanizer library you can change Roman numerals to numbers using the FromRoman extension
"IV".FromRoman() => 4
And reverse operation using the ToRoman extension.
4.ToRoman() => "IV"
Borrowed a lot from System.Linq on this one. String implements IEnumerable<char>, so I figured that was appropriate since we are treating it as an enumerable object anyways. Tested it against a bunch of random numbers, including 1, 3, 4, 8, 83, 99, 404, 555, 846, 927, 1999, 2420.
public static IDictionary<char, int> CharValues
{
get
{
return new Dictionary<char, int>
{{'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}};
}
}
public static int RomanNumeralToInteger(IEnumerable<char> romanNumerals)
{
int retVal = 0;
//go backwards
for (int i = romanNumerals.Count() - 1; i >= 0; i--)
{
//get current character
char c = romanNumerals.ElementAt(i);
//error checking
if (!CharValues.ContainsKey(c)) throw new InvalidRomanNumeralCharacterException(c);
//determine if we are adding or subtracting
bool op = romanNumerals.Skip(i).Any(rn => CharValues[rn] > CharValues[c]);
//then do so
retVal = op ? retVal - CharValues[c] : retVal + CharValues[c];
}
return retVal;
}
Solution with fulfilling the "subtractive notation" semantics checks
None of the current solutions completely fulfills the entire set of rules
for the "subtractive notation". "IIII" -> is not possible. Each of the solutions results a 4. Also the strings: "CCCC", "VV", "IC", "IM" are invalid.
A good online-converter to check the semantics is https://www.romannumerals.org/converter
So, if you really want doing a completely semantics-check, it's much more complex.
My approach was to first write the unit tests with the semantic checks. Then to write the code. Then to reduce the loops with some linq expressions.
Maybe there is a smarter solution, but I think the following code fullfills the rules to convert a roman numerals string.
After the code section, there is a section with my unit tests.
public class RomanNumerals
{
private List<Tuple<char, ushort, char?[]>> _validNumerals = new List<Tuple<char, ushort, char?[]>>()
{
new Tuple<char, ushort, char?[]>('I', 1, new char? [] {'V', 'X'}),
new Tuple<char, ushort, char?[]>('V', 5, null),
new Tuple<char, ushort, char?[]>('X', 10, new char?[] {'L', 'C'}),
new Tuple<char, ushort, char?[]>('L', 50, null),
new Tuple<char, ushort, char?[]>('C', 100, new char? [] {'D', 'M'}),
new Tuple<char, ushort, char?[]>('D', 500, null),
new Tuple<char, ushort, char?[]>('M', 1000, new char? [] {null, null})
};
public int TranslateRomanNumeral(string input)
{
var inputList = input?.ToUpper().ToList();
if (inputList == null || inputList.Any(x => _validNumerals.Select(t => t.Item1).Contains(x) == false))
{
throw new ArgumentException();
}
char? valForSubtraction = null;
int result = 0;
bool noAdding = false;
int equalSum = 0;
for (int i = 0; i < inputList.Count; i++)
{
var currentNumeral = _validNumerals.FirstOrDefault(s => s.Item1 == inputList[i]);
var nextNumeral = i < inputList.Count - 1 ? _validNumerals.FirstOrDefault(s => s.Item1 == inputList[i + 1]) : null;
bool currentIsDecimalPower = currentNumeral?.Item3?.Any() ?? false;
if (nextNumeral != null)
{
// Syntax and Semantics checks
if ((currentNumeral.Item2 < nextNumeral.Item2) && (currentIsDecimalPower == false || currentNumeral.Item3.Any(s => s == nextNumeral.Item1) == false) ||
(currentNumeral.Item2 == nextNumeral.Item2) && (currentIsDecimalPower == false || nextNumeral.Item1 == valForSubtraction) ||
(currentIsDecimalPower && result > 0 && ((nextNumeral.Item2 -currentNumeral.Item2) > result )) ||
(currentNumeral.Item2 > nextNumeral.Item2) && (nextNumeral.Item1 == valForSubtraction)
)
{
throw new ArgumentException();
}
if (currentNumeral.Item2 == nextNumeral.Item2)
{
equalSum += equalSum == 0 ? currentNumeral.Item2 + nextNumeral.Item2 : nextNumeral.Item2;
int? smallest = null;
var list = _validNumerals.Where(p => _validNumerals.FirstOrDefault(s => s.Item1 == currentNumeral.Item1).Item3.Any(s2 => s2 != null && s2 == p.Item1)).ToList();
if (list.Any())
{
smallest = list.Select(s3 => s3.Item2).ToList().Min();
}
// Another Semantics check
if (currentNumeral.Item3 != null && equalSum >= (smallest - currentNumeral.Item2))
{
throw new ArgumentException();
}
result += noAdding ? 0 : currentNumeral.Item2 + nextNumeral.Item2;
noAdding = !noAdding;
valForSubtraction = null;
}
else
if (currentNumeral.Item2 < nextNumeral.Item2)
{
equalSum = 0;
result += nextNumeral.Item2 - currentNumeral.Item2;
valForSubtraction = currentNumeral.Item1;
noAdding = true;
}
else
if (currentNumeral.Item2 > nextNumeral.Item2)
{
equalSum = 0;
result += noAdding ? 0 : currentNumeral.Item2;
noAdding = false;
valForSubtraction = null;
}
}
else
{
result += noAdding ? 0 : currentNumeral.Item2;
}
}
return result;
}
}
Here are the UNIT tests
[TestFixture]
public class RomanNumeralsTests
{
[Test]
public void TranslateRomanNumeral_WhenArgumentIsNull_RaiseArgumentNullException()
{
var romanNumerals = new RomanNumerals();
Assert.Throws<ArgumentException>(() => romanNumerals.TranslateRomanNumeral(null));
}
[TestCase("A")]
[TestCase("-")]
[TestCase("BXA")]
[TestCase("MMXK")]
public void TranslateRomanNumeral_WhenInvalidNumeralSyntax_RaiseException(string input)
{
var romanNumerals = new RomanNumerals();
Assert.Throws<ArgumentException>(() => romanNumerals.TranslateRomanNumeral(input));
}
[TestCase("IIII")]
[TestCase("CCCC")]
[TestCase("VV")]
[TestCase("IC")]
[TestCase("IM")]
[TestCase("XM")]
[TestCase("IL")]
[TestCase("MCDXCXI")]
[TestCase("MCDDXC")]
public void TranslateRomanNumeral_WhenInvalidNumeralSemantics_RaiseException(string input)
{
var romanNumerals = new RomanNumerals();
Assert.Throws<ArgumentException>(() => romanNumerals.TranslateRomanNumeral(input));
}
[TestCase("I", 1)]
[TestCase("II", 2)]
[TestCase("III", 3)]
[TestCase("IV", 4)]
[TestCase("XLII", 42)]
[TestCase("MMXIII", 2013)]
[TestCase("MXI", 1011)]
[TestCase("MCDXCIX", 1499)]
[TestCase("MMXXII", 2022)]
[TestCase("V", 5)]
[TestCase("VI", 6)]
[TestCase("CX", 110)]
[TestCase("CCCLXXV", 375)]
[TestCase("MD", 1500)]
[TestCase("MDLXXV", 1575)]
[TestCase("MDCL", 1650)]
[TestCase("MDCCXXV", 1725)]
[TestCase("MDCCC", 1800)]
[TestCase("MDCCCLXXV", 1875)]
[TestCase("MCML", 1950)]
[TestCase("MMXXV", 2025)]
[TestCase("MMC", 2100)]
[TestCase("MMCLXXV", 2175)]
[TestCase("MMCCL", 2250)]
[TestCase("MMCCCXXV", 2325)]
[TestCase("MMCD", 2400)]
[TestCase("MMCDLXXV", 2475)]
[TestCase("MMDL", 2550)]
[TestCase("MMMMMMMM", 8000)]
[TestCase("MMMMMMMMIV", 8004)]
public void TranslateRomanNumeral_WhenValidNumeral_Translate(string input, int output)
{
var romanNumerals = new RomanNumerals();
var result = romanNumerals.TranslateRomanNumeral(input);
Assert.That(result.Equals(output));
}
}
private static Dictionary<char, int> RomanNumberMap = new Dictionary<char, int> {
{'I', 1},
{'V', 5},
{'X', 10},
{'L', 50},
{'C', 100},
{'D', 500},
{'M', 1000}
};
private const string RomanNumberValidationRegEx = "^(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$";
private static int ConvertToRomanNumberToInteger(string romanNumber)
{
if (!Regex.IsMatch(romanNumber, RomanNumberValidationRegEx))
{
throw new ArgumentOutOfRangeException(romanNumber);
}
int result = 0;
for (int i = 0; i < romanNumber.Length; i++)
{
int currentVal = RomanNumberMap[romanNumber[i]];
if (romanNumber.Length > i + 1)
{
int nextVal = RomanNumberMap[romanNumber[i + 1]];
if (nextVal > currentVal)
{
result = result + (nextVal - currentVal);
i++;
continue;
}
}
result = result + currentVal;
}
return result;
}
I will suggest a simplest method for this by using array in .net : comments are given in C# section for explanation
VB.net
Public Class Form1
Dim indx() As Integer = {1, 2, 3, 4, 5, 10, 50, 100, 500, 1000}
Dim row() As String = {"I", "II", "III", "IV", "V", "X", "L", "C", "D", "M"}
Dim limit As Integer = 9
Dim output As String = ""
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim num As Integer
output = ""
num = CInt(txt1.Text)
While num > 0
num = find(num)
End While
txt2.Text = output
End Sub
Public Function find(ByVal Num As Integer) As Integer
Dim i As Integer = 0
While indx(i) <= Num
i += 1
End While
If i <> 0 Then
limit = i - 1
Else
limit = 0
End If
output = output & row(limit)
Num = Num - indx(limit)
Return Num
End Function
End Class
C#
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class Form1
{
int[] indx = {
1,
2,
3,
4,
5,
10,
50,
100,
500,
1000
// initialize array of integers
};
string[] row = {
"I",
"II",
"III",
"IV",
"V",
"X",
"L",
"C",
"D",
"M"
//Carasponding roman letters in for the numbers in the array
};
// integer to indicate the position index for link two arrays
int limit = 9;
//string to store output
string output = "";
private void Button1_Click(System.Object sender, System.EventArgs e)
{
int num = 0;
// stores the input
output = "";
// clear output before processing
num = Convert.ToInt32(txt1.Text);
// get integer value from the textbox
//Loop until the value became 0
while (num > 0) {
num = find(num);
//call function for processing
}
txt2.Text = output;
// display the output in text2
}
public int find(int Num)
{
int i = 0;
// loop variable initialized with 0
//Loop until the indx(i).value greater than or equal to num
while (indx(i) <= Num) {
i += 1;
}
// detemine the value of limit depends on the itetration
if (i != 0) {
limit = i - 1;
} else {
limit = 0;
}
output = output + row(limit);
//row(limit) is appended with the output
Num = Num - indx(limit);
// calculate next num value
return Num;
//return num value for next itetration
}
}
I refer from this blog. You could just reverse the roman numeral , then all the thing would be more easier compare to make the comparison.
public static int pairConversion(int dec, int lastNum, int lastDec)
{
if (lastNum > dec)
return lastDec - dec;
else return lastDec + dec;
}
public static int ConvertRomanNumtoInt(string strRomanValue)
{
var dec = 0;
var lastNum = 0;
foreach (var c in strRomanValue.Reverse())
{
switch (c)
{
case 'I':
dec = pairConversion(1, lastNum, dec);
lastNum = 1;
break;
case 'V':
dec=pairConversion(5,lastNum, dec);
lastNum = 5;
break;
case 'X':
dec = pairConversion(10, lastNum, dec);
lastNum = 10;
break;
case 'L':
dec = pairConversion(50, lastNum, dec);
lastNum = 50;
break;
case 'C':
dec = pairConversion(100, lastNum, dec);
lastNum = 100;
break;
case 'D':
dec = pairConversion(500, lastNum, dec);
lastNum = 500;
break;
case 'M':
dec = pairConversion(1000, lastNum, dec);
lastNum = 1000;
break;
}
}
return dec;
}
This one uses a stack:
public int RomanToInt(string s)
{
var dict = new Dictionary<char, int>();
dict['I'] = 1;
dict['V'] = 5;
dict['X'] = 10;
dict['L'] = 50;
dict['C'] = 100;
dict['D'] = 500;
dict['M'] = 1000;
Stack<char> st = new Stack<char>();
foreach (char ch in s.ToCharArray())
st.Push(ch);
int result = 0;
while (st.Count > 0)
{
var c1=st.Pop();
var ch1 = dict[c1];
if (st.Count > 0)
{
var c2 = st.Peek();
var ch2 = dict[c2];
if (ch2 < ch1)
{
result += (ch1 - ch2);
st.Pop();
}
else
{
result += ch1;
}
}
else
{
result += ch1;
}
}
return result;
}
I wrote this just using arrays.
I omit the testing code here, but it looks it works properly.
public static class RomanNumber {
static string[] units = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" };
static string[] tens = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" };
static string[] hundreds = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" };
static string[] thousands = { "", "M", "MM", "MMM" };
static public bool IsRomanNumber(string source) {
try {
return RomanNumberToInt(source) > 0;
}
catch {
return false;
}
}
/// <summary>
/// Parses a string containing a roman number.
/// </summary>
/// <param name="source">source string</param>
/// <returns>The integer value of the parsed roman numeral</returns>
/// <remarks>
/// Throws an exception on invalid source.
/// Throws an exception if source is not a valid roman number.
/// Supports roman numbers from "I" to "MMMCMXCIX" ( 1 to 3999 )
/// NOTE : "IMMM" is not valid</remarks>
public static int RomanNumberToInt(string source) {
if (String.IsNullOrWhiteSpace(source)) {
throw new ArgumentNullException();
}
int total = 0;
string buffer = source;
// parse the last four characters in the string
// each time we check the buffer against a number array,
// starting from units up to thousands
// we quit as soon as there are no remaing characters to parse
total += RipOff(buffer, units, out buffer);
if (buffer != null) {
total += (RipOff(buffer, tens, out buffer)) * 10;
}
if (buffer != null) {
total += (RipOff(buffer, hundreds, out buffer)) * 100;
}
if (buffer != null) {
total += (RipOff(buffer, thousands, out buffer)) * 1000;
}
// after parsing for thousands, if there is any character left, this is not a valid roman number
if (buffer != null) {
throw new ArgumentException(String.Format("{0} is not a valid roman number", buffer));
}
return total;
}
/// <summary>
/// Given a string, takes the four characters on the right,
/// search an element in the numbers array and returns the remaing characters.
/// </summary>
/// <param name="source">source string to parse</param>
/// <param name="numbers">array of roman numerals</param>
/// <param name="left">remaining characters on the left</param>
/// <returns>If it finds a roman numeral returns its integer value; otherwise returns zero</returns>
public static int RipOff(string source, string[] numbers, out string left) {
int result = 0;
string buffer = null;
// we take the last four characters : this is the length of the longest numeral in our arrays
// ("VIII", "LXXX", "DCCC")
// or all if source length is 4 or less
if (source.Length > 4) {
buffer = source.Substring(source.Length - 4);
left = source.Substring(0, source.Length - 4);
}
else {
buffer = source;
left = null;
}
// see if buffer exists in the numbers array
// if it does not, skip the first character and try again
// until buffer contains only one character
// append the skipped character to the left arguments
while (!numbers.Contains(buffer)) {
if (buffer.Length == 1) {
left = source; // failed
break;
}
else {
left += buffer.Substring(0, 1);
buffer = buffer.Substring(1);
}
}
if (buffer.Length > 0) {
if (numbers.Contains(buffer)) {
result = Array.IndexOf(numbers, buffer);
}
}
return result;
}
}
}
EDIT
Forget about it !
Just look at BrunoLM solution here.
It's simple and elegant.
The only caveat is that it does not check the source.
This is my solution:
/// <summary>
/// Converts a Roman number string into a Arabic number
/// </summary>
/// <param name="romanNumber">the Roman number string</param>
/// <returns>the Arabic number (0 if the given string is not convertible to a Roman number)</returns>
public static int ToArabicNumber(string romanNumber)
{
string[] replaceRom = { "CM", "CD", "XC", "XL", "IX", "IV" };
string[] replaceNum = { "DCCCC", "CCCC", "LXXXX", "XXXX", "VIIII", "IIII" };
string[] roman = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
int[] arabic = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
return Enumerable.Range(0, replaceRom.Length)
.Aggregate
(
romanNumber,
(agg, cur) => agg.Replace(replaceRom[cur], replaceNum[cur]),
agg => agg.ToArray()
)
.Aggregate
(
0,
(agg, cur) =>
{
int idx = Array.IndexOf(roman, cur.ToString());
return idx < 0 ? 0 : agg + arabic[idx];
},
agg => agg
);
}
/// <summary>
/// Converts a Arabic number into a Roman number string
/// </summary>
/// <param name="arabicNumber">the Arabic number</param>
/// <returns>the Roman number string</returns>
public static string ToRomanNumber(int arabicNumber)
{
string[] roman = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
int[] arabic = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
return Enumerable.Range(0, arabic.Length)
.Aggregate
(
Tuple.Create(arabicNumber, string.Empty),
(agg, cur) =>
{
int remainder = agg.Item1 % arabic[cur];
string concat = agg.Item2 + string.Concat(Enumerable.Range(0, agg.Item1 / arabic[cur]).Select(num => roman[cur]));
return Tuple.Create(remainder, concat);
},
agg => agg.Item2
);
}
Here's the Explanation how the methods work:
ToArabicNumber
First aggregation step is to Replace the Roman Number special cases (e.g.: IV -> IIII). Second Aggregate step simply sums up the equivalent Arabic number of the Roman letter (e.g. V -> 5)
ToRomanNumber:
I start the aggregation with the given Arabic number. For each step the number will be divided by the equivalent number of the Roman letter. The remainder of this division is then the input for the next step. The division Result will be translated to the Equivalent Roman Number character which will be appended to the result string.
private static HashMap<Character, Integer> romanMap = new HashMap<>() {{
put('I', 1); put('V', 5); put('X', 10); put('L', 50);
put('C', 100); put('D', 500); put('M', 1000);
}};
private static int convertRomanToInt(String romanNumeral) {
int total = 0;
romanNumeral = romanNumeral.toUpperCase();
//add every Roman numeral
for(int i = 0; i < romanNumeral.length(); i++) {
total += romanMap.get(romanNumeral.charAt(i));
}
//remove the Roman numerals that are followed
//directly by a larger Roman numeral
for(int i = 0; i < romanNumeral.length()-1; i++) {
if(romanMap.get(romanNumeral.charAt(i))
< romanMap.get(romanNumeral.charAt(i+1))) {
total -= 2* romanMap.get(romanNumeral.charAt(i));
}
}
return total;
}
//note that the topmost solution does not solve this Roman numeral
//but mine does
//also note that this solution is a preference of simplicity over complexity
public static void main(String[] args) {
String rn = "CcLXxiV"; //274
System.out.println("Convert " + rn + " to " + convertRomanToInt(rn));
}
/*
this uses the string object Replace() & Split() methods
*/
int ToNumber(string roman){
/*
the 0 padding after the comma delimiter allows adding up the extra index produced by Split, which is not numerical
*/
string s1=roman.Replace("CM","900,0");
s1=s1.Replace("M","1000,0");
s1=s1.Replace("CD","400,0");
s1=s1.Replace("D","500,0");
s1=s1.Replace("XC","90,0");
s1=s1.Replace("C","100,0");
s1=s1.Replace("XL","40,0");
s1=s1.Replace("L","50,0");
s1=s1.Replace("IX","9,0");
s1=s1.Replace("X","10,0");
s1=s1.Replace("IV","4,0");
s1=s1.Replace("V","5,0");
s1=s1.Replace("I","1,0");
string[] spl=s1.Split(",");
int rlt=0;
for(int i=0;i<spl.Count();i++)
{
rlt+= Convert.ToInt32(spl[i].ToString());
}
return rlt;
}
Here is my O(n) solution in JavaScript
const NUMERAL = {
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000,
}
var romanToInt = function(s) {
if (s.length === 1) return +NUMERAL[s];
let number = 0;
for (let i = 0; i < s.length; i++) {
let num = +NUMERAL[s[i]];
let prev = +NUMERAL[s[i-1]];
if (prev < num) number += num - (2 * prev);
else number += num;
}
return number;
};
FWIW, here is a "try parse" version of David DeMar's answer:
private static readonly Dictionary<char, int> _romanMap = new Dictionary<char, int>() { { 'I', 1 }, { 'V', 5 }, { 'X', 10 }, { 'L', 50 }, { 'C', 100 }, { 'D', 500 }, { 'M', 1000 } };
public static bool TryParseRoman(string text, out int value)
{
value = 0;
if (string.IsNullOrEmpty(text))
return false;
var number = 0;
for (var i = 0; i < text.Length; i++)
{
if (!_romanMap.TryGetValue(text[i], out var num))
return false;
if ((i + 1) < text.Length)
{
if (!_romanMap.TryGetValue(text[i + 1], out var num2))
return false;
if (num < num2)
{
number -= num;
continue;
}
}
number += num;
}
value = number;
return true;
}
public class Solution {
public int RomanToInt(string s) {
var dict = new Dictionary<char,int>{
{'I',1},
{'V',5},
{'X',10},
{'L',50},
{'C',100},
{'M',1000},
{'D',500},
};
var result = 0; //What am I gonna return it from method ?
for(int i=0; i<s.Length; i++)
{
if(i+1<s.Length && dict[s[i]] < dict[s[i+1]])
{
result -=dict[s[i]];
}
else
{
result +=dict[s[i]];
}
}
return result;
}
}
public int RomanToInt(string s)
{
char[] romans = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
int[] nums = { 1, 5, 10, 50, 100, 500, 1000 };
int result = 0;
foreach (char c in s)
{
for (int i = 0; i < romans.Length; i++)
{
if (romans[i] == c)
{
result += nums[i];
}
}
}
if (s.Contains("IV") || s.Contains("IX"))
result -= 2;
if (s.Contains("XL") || s.Contains("XC"))
result -= 20;
if (s.Contains("CD") || s.Contains("CM"))
result -= 200;
return result;
}
public static int ConvertRomanNumtoInt(string strRomanValue)
{
Dictionary RomanNumbers = new Dictionary
{
{"M", 1000},
{"CM", 900},
{"D", 500},
{"CD", 400},
{"C", 100},
{"XC", 90},
{"L", 50},
{"XL", 40},
{"X", 10},
{"IX", 9},
{"V", 5},
{"IV", 4},
{"I", 1}
};
int retVal = 0;
foreach (KeyValuePair pair in RomanNumbers)
{
while (strRomanValue.IndexOf(pair.Key.ToString()) == 0)
{
retVal += int.Parse(pair.Value.ToString());
strRomanValue = strRomanValue.Substring(pair.Key.ToString().Length);
}
}
return retVal;
}

How to convert hex to a byte array?

I copied and pasted this binary data out of sql server, which I am unable to query at this time.
0xBAC893CAB8B7FE03C927417A2A3F6A60BD30FF35E250011CB25507EBFCD5223B
How do I convert it back to a byte array in c#?
Something like this:
using System;
public static class Parser
{
static void Main()
{
string hex = "0xBAC893CAB8B7FE03C927417A2A3F6A6"
+ "0BD30FF35E250011CB25507EBFCD5223B";
byte[] parsed = ParseHex(hex);
// Just for confirmation...
Console.WriteLine(BitConverter.ToString(parsed));
}
public static byte[] ParseHex(string hex)
{
int offset = hex.StartsWith("0x") ? 2 : 0;
if ((hex.Length % 2) != 0)
{
throw new ArgumentException("Invalid length: " + hex.Length);
}
byte[] ret = new byte[(hex.Length-offset)/2];
for (int i=0; i < ret.Length; i++)
{
ret[i] = (byte) ((ParseNybble(hex[offset]) << 4)
| ParseNybble(hex[offset+1]));
offset += 2;
}
return ret;
}
static int ParseNybble(char c)
{
if (c >= '0' && c <= '9')
{
return c-'0';
}
if (c >= 'A' && c <= 'F')
{
return c-'A'+10;
}
if (c >= 'a' && c <= 'f')
{
return c-'a'+10;
}
throw new ArgumentException("Invalid hex digit: " + c);
}
}
(EDIT: Now slightly more efficient - no substrings required...)
It's possible that ParseNybble could be more efficient. For example, a switch/case may be more efficient:
static int ParseNybble(char c)
{
switch (c)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return c-'0';
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
return c-'A'+10;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
return c-'a'+10;
}
throw new ArgumentException("Invalid hex digit: " + c);
}
or possibly a lookup array:
// Omitted for brevity... I'm sure you get the gist
private static readonly int[] NybbleLookup = BuildLookup();
private int ParseNybble(char c)
{
if (c > 'f')
{
throw new ArgumentException("Invalid hex digit: " + c);
}
int ret = NybbleLookup[c];
if (ret == -1)
{
throw new ArgumentException("Invalid hex digit: " + c);
}
return ret;
}
I haven't benchmarked any of these, and I've no idea which would be the fastest. The current solution is probably the simplest though.
Consider leveraging a Framework class that already exposes the ability to perform hex conversion, XmlReader for example:
public static byte[] HexToBytes(this string hexEncodedBytes, int start, int end)
{
int length = end - start;
const string tagName = "hex";
string fakeXmlDocument = String.Format("<{1}>{0}</{1}>",
hexEncodedBytes.Substring(start, length),
tagName);
var stream = new MemoryStream(Encoding.ASCII.GetBytes(fakeXmlDocument));
XmlReader reader = XmlReader.Create(stream, new XmlReaderSettings());
int hexLength = length / 2;
byte[] result = new byte[hexLength];
reader.ReadStartElement(tagName);
reader.ReadContentAsBinHex(result, 0, hexLength);
return result;
}
usage:
string input = "0xBAC893CAB8B7FE03C927417A2A3F6A60BD30FF35E250011CB255";
byte[] bytes = input.HexToBytes(2, input.Length);
Simple:
string hexnum = "0000000F"; // Represents 15
int value = int.Parse(hexnum, System.Globalization.NumberStyles.HexNumber);
All you have to remember to do is for an int to divide the hex number up into groups of 8 hex digits (hex are 4 bits each, and CLR int type is 32 bits, hence 8 digits per int). There's also a byte.Parse() that works the same, but pass in two hex digits at a time.
Something like this:
public byte[] ParseHexString(string text)
{
if ((text.Length % 2) != 0)
{
throw new ArgumentException("Invalid length: " + text.Length);
}
if (text.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase))
{
text = text.Substring(2);
}
int arrayLength = text.Length / 2;
byte[] byteArray = new byte[arrayLength];
for (int i = 0; i < arrayLength; i++)
{
byteArray[i] = byte.Parse(text.Substring(i*2, 2), NumberStyles.HexNumber);
}
return byteArray;
}
You will need to modify this a little bit (for example, skip over the first two characters), but it does handle spaces in the string:
/// <summary>
/// Decodes a hex string, ignoring all non-hex characters, and stores
/// the decodes series of bytes into the shared buffer. This returns
/// the number of bytes that were decoded.
/// <para>Hex characters are [0-9, a-f, A-F].</para>
/// </summary>
/// <param name="hexString">String to parse into bytes.</param>
/// <param name="buffer">Buffer into which to store the decoded binary data.</param>
/// <returns>The number of bytes decoded.</returns>
private static int DecodeHexIntoBuffer(string hexString, byte[] buffer)
{
int count = 0;
bool haveFirst = false;
bool haveSecond = false;
char first = '0';
char second = '0';
for (int i = 0; i < hexString.Length; i++)
{
if (!haveFirst)
{
first = hexString[i];
haveFirst = char.IsLetterOrDigit(first);
// we have to continue to the next iteration
// or we will miss characters
continue;
}
if (!haveSecond)
{
second = hexString[i];
haveSecond = char.IsLetterOrDigit(second);
}
if (haveFirst && haveSecond)
{
string hex = "" + first + second;
byte nextByte;
if (byte.TryParse(hex, NumberStyles.HexNumber, null, out nextByte))
{
// store the decoded byte into the next slot of the buffer
buffer[count++] = nextByte;
}
// reset the flags
haveFirst = haveSecond = false;
}
}
return count;
}
Actually, there's an easier way to convert two characters at a time to a byte:
/// <summary>
/// This will convert a hex-encoded string to byte data
/// </summary>
/// <param name="hexData">The hex-encoded string to convert</param>
/// <returns>The bytes that make up the hex string</returns>
public static byte[] FromHex(string hexData)
{
List<byte> data = new List<byte>();
string byteSet = string.Empty;
int stringLen = hexData.Length;
int length = 0;
for (int i = 0; i < stringLen; i = i + 2)
{
length = (stringLen - i) > 1 ? 2 : 1;
byteSet = hexData.Substring(i, length);
// try and parse the data
data.Add(Convert.ToByte(byteSet, 16 /*base*/));
} // next set
return data.ToArray();
}
Slow yet fun way :D
public static byte[] StringToByteArray(string hex)
{
hex = hex.Replace(" ", "");
hex = hex.Replace(":", "");
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
-jD
I use this for C#, from similar code in Java.
private static char[] hexdigit = "0123456789abcdef".ToCharArray();
public static string hexlify(string argbuf) {
int arglen = argbuf.Length;
char[] argca = argbuf.ToCharArray ();
StringBuilder retbuf = new StringBuilder(arglen * 2);
for (int i = 0; i < arglen; i++) {
char ch = argca[i];
retbuf.Append(hexdigit[(ch >> 4) & 0xF]);
retbuf.Append(hexdigit[ch & 0xF]);
}
return retbuf.ToString();
}
public static string unhexlify(string argbuf) {
int arglen = argbuf.Length;
if (arglen % 2 != 0) {
throw new ArgumentOutOfRangeException ("Odd-length string");
}
char[] argca = argbuf.ToCharArray ();
StringBuilder retbuf = new StringBuilder(arglen / 2);
for (int i = 0; i < arglen; i += 2) {
int top = Convert.ToInt32 (argca[i].ToString (), 16);
int bot = Convert.ToInt32 (argca[i + 1].ToString (), 16);
if (top == -1 || bot == -1) {
throw new ArgumentOutOfRangeException ("Non-hexadecimal digit found");
}
retbuf.Append((char) ((top << 4) + bot));
}
return retbuf.ToString();
}
maybe this one is cute!
string source = "Hello World!";
using (SHA256 sha256Hash = SHA256.Create())
{
//From String to byte array
byte[] sourceBytes = Encoding.UTF8.GetBytes(source);
byte[] hashBytes = sha256Hash.ComputeHash(sourceBytes);
string hash = BitConverter.ToString(hashBytes).Replace("-", String.Empty);
Console.WriteLine("The SHA256 hash of " + source + " is: " + hash);
}

How to convert numbers between hexadecimal and decimal

How do you convert between hexadecimal numbers and decimal numbers in C#?
To convert from decimal to hex do...
string hexValue = decValue.ToString("X");
To convert from hex to decimal do either...
int decValue = int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber);
or
int decValue = Convert.ToInt32(hexValue, 16);
Hex -> decimal:
Convert.ToInt64(hexString, 16);
Decimal -> Hex
string.Format("{0:x}", intValue);
It looks like you can say
Convert.ToInt64(value, 16)
to get the decimal from hexdecimal.
The other way around is:
otherVar.ToString("X");
If you want maximum performance when doing conversion from hex to decimal number, you can use the approach with pre-populated table of hex-to-decimal values.
Here is the code that illustrates that idea. My performance tests showed that it can be 20%-40% faster than Convert.ToInt32(...):
class TableConvert
{
static sbyte[] unhex_table =
{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};
public static int Convert(string hexNumber)
{
int decValue = unhex_table[(byte)hexNumber[0]];
for (int i = 1; i < hexNumber.Length; i++)
{
decValue *= 16;
decValue += unhex_table[(byte)hexNumber[i]];
}
return decValue;
}
}
From Geekpedia:
// Store integer 182
int decValue = 182;
// Convert integer 182 as a hex in a string variable
string hexValue = decValue.ToString("X");
// Convert the hex string back to the number
int decAgain = int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber);
String stringrep = myintvar.ToString("X");
int num = int.Parse("FF", System.Globalization.NumberStyles.HexNumber);
If it's a really big hex string beyond the capacity of the normal integer:
For .NET 3.5, we can use BouncyCastle's BigInteger class:
String hex = "68c7b05d0000000002f8";
// results in "494809724602834812404472"
String decimal = new Org.BouncyCastle.Math.BigInteger(hex, 16).ToString();
.NET 4.0 has the BigInteger class.
Hex to Decimal Conversion
Convert.ToInt32(number, 16);
Decimal to Hex Conversion
int.Parse(number, System.Globalization.NumberStyles.HexNumber)
For more details Check this article
Try using BigNumber in C# - Represents an arbitrarily large signed integer.
Program
using System.Numerics;
...
var bigNumber = BigInteger.Parse("837593454735734579347547357233757342857087879423437472347757234945743");
Console.WriteLine(bigNumber.ToString("X"));
Output
4F30DC39A5B10A824134D5B18EEA3707AC854EE565414ED2E498DCFDE1A15DA5FEB6074AE248458435BD417F06F674EB29A2CFECF
Possible Exceptions,
ArgumentNullException - value is null.
FormatException - value is not in the correct format.
Conclusion
You can convert string and store a value in BigNumber without constraints about the size of the number unless the string is empty and non-analphabets
static string chex(byte e) // Convert a byte to a string representing that byte in hexadecimal
{
string r = "";
string chars = "0123456789ABCDEF";
r += chars[e >> 4];
return r += chars[e &= 0x0F];
} // Easy enough...
static byte CRAZY_BYTE(string t, int i) // Take a byte, if zero return zero, else throw exception (i=0 means false, i>0 means true)
{
if (i == 0) return 0;
throw new Exception(t);
}
static byte hbyte(string e) // Take 2 characters: these are hex chars, convert it to a byte
{ // WARNING: This code will make small children cry. Rated R.
e = e.ToUpper(); //
string msg = "INVALID CHARS"; // The message that will be thrown if the hex str is invalid
byte[] t = new byte[] // Gets the 2 characters and puts them in seperate entries in a byte array.
{ // This will throw an exception if (e.Length != 2).
(byte)e[CRAZY_BYTE("INVALID LENGTH", e.Length ^ 0x02)],
(byte)e[0x01]
};
for (byte i = 0x00; i < 0x02; i++) // Convert those [ascii] characters to [hexadecimal] characters. Error out if either character is invalid.
{
t[i] -= (byte)((t[i] >= 0x30) ? 0x30 : CRAZY_BYTE(msg, 0x01)); // Check for 0-9
t[i] -= (byte)((!(t[i] < 0x0A)) ? (t[i] >= 0x11 ? 0x07 : CRAZY_BYTE(msg, 0x01)) : 0x00); // Check for A-F
}
return t[0x01] |= t[0x00] <<= 0x04; // The moment of truth.
}
This is not really easiest way but this source code enable you to right any types of octal number i.e 23.214, 23 and 0.512 and so on. Hope this will help you..
public string octal_to_decimal(string m_value)
{
double i, j, x = 0;
Int64 main_value;
int k = 0;
bool pw = true, ch;
int position_pt = m_value.IndexOf(".");
if (position_pt == -1)
{
main_value = Convert.ToInt64(m_value);
ch = false;
}
else
{
main_value = Convert.ToInt64(m_value.Remove(position_pt, m_value.Length - position_pt));
ch = true;
}
while (k <= 1)
{
do
{
i = main_value % 10; // Return Remainder
i = i * Convert.ToDouble(Math.Pow(8, x)); // calculate power
if (pw)
x++;
else
x--;
o_to_d = o_to_d + i; // Saving Required calculated value in main variable
main_value = main_value / 10; // Dividing the main value
}
while (main_value >= 1);
if (ch)
{
k++;
main_value = Convert.ToInt64(Reversestring(m_value.Remove(0, position_pt + 1)));
}
else
k = 2;
pw = false;
x = -1;
}
return (Convert.ToString(o_to_d));
}
This one worked for me:
public static decimal HexToDec(string hex)
{
if (hex.Length % 2 == 1)
hex = "0" + hex;
byte[] raw = new byte[hex.Length / 2];
decimal d = 0;
for (int i = 0; i < raw.Length; i++)
{
raw[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
d += Math.Pow(256, (raw.Length - 1 - i)) * raw[i];
}
return d.ToString();
return d;
}
Decimal - Hexa
var decValue = int.Parse(Console.ReadLine());
string hex = string.Format("{0:x}", decValue);
Console.WriteLine(hex);
Hexa - Decimal (use namespace: using System.Globalization;)
var hexval = Console.ReadLine();
int decValue = int.Parse(hexval, NumberStyles.HexNumber);
Console.WriteLine(decValue);
FOUR C# native ways to convert Hex to Dec and back:
using System;
namespace Hexadecimal_and_Decimal
{
internal class Program
{
private static void Main(string[] args)
{
string hex = "4DEAD";
int dec;
// hex to dec:
dec = int.Parse(hex, System.Globalization.NumberStyles.HexNumber);
// or:
dec = Convert.ToInt32(hex, 16);
// dec to hex:
hex = dec.ToString("X"); // lowcase: x, uppercase: X
// or:
hex = string.Format("{0:X}", dec); // lowcase: x, uppercase: X
Console.WriteLine("Hexadecimal number: " + hex);
Console.WriteLine("Decimal number: " + dec);
}
}
}
My version is I think a little more understandable because my C# knowledge is not so high.
I'm using this algorithm: http://easyguyevo.hubpages.com/hub/Convert-Hex-to-Decimal (The Example 2)
using System;
using System.Collections.Generic;
static class Tool
{
public static string DecToHex(int x)
{
string result = "";
while (x != 0)
{
if ((x % 16) < 10)
result = x % 16 + result;
else
{
string temp = "";
switch (x % 16)
{
case 10: temp = "A"; break;
case 11: temp = "B"; break;
case 12: temp = "C"; break;
case 13: temp = "D"; break;
case 14: temp = "E"; break;
case 15: temp = "F"; break;
}
result = temp + result;
}
x /= 16;
}
return result;
}
public static int HexToDec(string x)
{
int result = 0;
int count = x.Length - 1;
for (int i = 0; i < x.Length; i++)
{
int temp = 0;
switch (x[i])
{
case 'A': temp = 10; break;
case 'B': temp = 11; break;
case 'C': temp = 12; break;
case 'D': temp = 13; break;
case 'E': temp = 14; break;
case 'F': temp = 15; break;
default: temp = -48 + (int)x[i]; break; // -48 because of ASCII
}
result += temp * (int)(Math.Pow(16, count));
count--;
}
return result;
}
}
class Program
{
static void Main(string[] args)
{
Console.Write("Enter Decimal value: ");
int decNum = int.Parse(Console.ReadLine());
Console.WriteLine("Dec {0} is hex {1}", decNum, Tool.DecToHex(decNum));
Console.Write("\nEnter Hexadecimal value: ");
string hexNum = Console.ReadLine().ToUpper();
Console.WriteLine("Hex {0} is dec {1}", hexNum, Tool.HexToDec(hexNum));
Console.ReadKey();
}
}
Convert binary to Hex
Convert.ToString(Convert.ToUInt32(binary1, 2), 16).ToUpper()
You can use this code and possible set Hex length and part's:
const int decimal_places = 4;
const int int_places = 4;
static readonly string decimal_places_format = $"X{decimal_places}";
static readonly string int_places_format = $"X{int_places}";
public static string DecimaltoHex(decimal number)
{
var n = (int)Math.Truncate(number);
var f = (int)Math.Truncate((number - n) * ((decimal)Math.Pow(10, decimal_places)));
return $"{string.Format($"{{0:{int_places_format}}}", n)}{string.Format($"{{0:{decimal_places_format}}}", f)}";
}
public static decimal HextoDecimal(string number)
{
var n = number.Substring(0, number.Length - decimal_places);
var f = number.Substring(number.Length - decimal_places);
return decimal.Parse($"{int.Parse(n, System.Globalization.NumberStyles.HexNumber)}.{int.Parse(f, System.Globalization.NumberStyles.HexNumber)}");
}
An extension method for converting a byte array into a hex representation. This pads each byte with leading zeros.
/// <summary>
/// Turns the byte array into its Hex representation.
/// </summary>
public static string ToHex(this byte[] y)
{
StringBuilder sb = new StringBuilder();
foreach (byte b in y)
{
sb.Append(b.ToString("X").PadLeft(2, "0"[0]));
}
return sb.ToString();
}
Here is my function:
using System;
using System.Collections.Generic;
class HexadecimalToDecimal
{
static Dictionary<char, int> hexdecval = new Dictionary<char, int>{
{'0', 0},
{'1', 1},
{'2', 2},
{'3', 3},
{'4', 4},
{'5', 5},
{'6', 6},
{'7', 7},
{'8', 8},
{'9', 9},
{'a', 10},
{'b', 11},
{'c', 12},
{'d', 13},
{'e', 14},
{'f', 15},
};
static decimal HexToDec(string hex)
{
decimal result = 0;
hex = hex.ToLower();
for (int i = 0; i < hex.Length; i++)
{
char valAt = hex[hex.Length - 1 - i];
result += hexdecval[valAt] * (int)Math.Pow(16, i);
}
return result;
}
static void Main()
{
Console.WriteLine("Enter Hexadecimal value");
string hex = Console.ReadLine().Trim();
//string hex = "29A";
Console.WriteLine("Hex {0} is dec {1}", hex, HexToDec(hex));
Console.ReadKey();
}
}
My solution is a bit like back to basics, but it works without using any built-in functions to convert between number systems.
public static string DecToHex(long a)
{
int n = 1;
long b = a;
while (b > 15)
{
b /= 16;
n++;
}
string[] t = new string[n];
int i = 0, j = n - 1;
do
{
if (a % 16 == 10) t[i] = "A";
else if (a % 16 == 11) t[i] = "B";
else if (a % 16 == 12) t[i] = "C";
else if (a % 16 == 13) t[i] = "D";
else if (a % 16 == 14) t[i] = "E";
else if (a % 16 == 15) t[i] = "F";
else t[i] = (a % 16).ToString();
a /= 16;
i++;
}
while ((a * 16) > 15);
string[] r = new string[n];
for (i = 0; i < n; i++)
{
r[i] = t[j];
j--;
}
string res = string.Concat(r);
return res;
}

Categories

Resources