What is the best way to compare two hexadecimal numbers (that is a string)? For instance,
string a = "3F";
string b = "32";
if (a > b)
MessageBox.Show("a is greater");
Should work. (Assuming > has been properly overloaded).
You can always convert them to ints and compare them that way:
int a = int.Parse("3E", System.Globalization.NumberStyles.HexNumber);
int b = int.Parse("32", System.Globalization.NumberStyles.HexNumber);
if (a > b)
MessageBox.Show("a is greater");
Seems safer :)
Convert them to integers and compare the integers.
There is also a simple algo based on String comparisson:
Assumed your numbers have a unique format: always lower case or higher case letters. Leading 0x or not, no leading zeros. Then you can do like this:
If number a has more digits than number b: a > b
If the number of digits is equal you could use String.Compare.
This algo has the advantage it is not limited to 32 or 64 bits.
Here is a fairly robust implementation of hendrik’s suggestion. There are a number of ways it could be optimized if your input strings have known attributes, but it should be able to compare valid hex strings of any size and/or with mixed formats.
public int HexStringCompare(string value1, string value2)
{
string InvalidHexExp = #"[^\dabcdef]";
string HexPaddingExp = #"^(0x)?0*";
//Remove whitespace, "0x" prefix if present, and leading zeros.
//Also make all characters lower case.
string Value1 = Regex.Replace(value1.Trim().ToLower(), HexPaddingExp, "");
string Value2 = Regex.Replace(value2.Trim().ToLower(), HexPaddingExp, "");
//validate that values contain only hex characters
if (Regex.IsMatch(Value1, InvalidHexExp))
{
throw new ArgumentOutOfRangeException("Value1 is not a hex string");
}
if (Regex.IsMatch(Value2, InvalidHexExp))
{
throw new ArgumentOutOfRangeException("Value2 is not a hex string");
}
int Result = Value1.Length.CompareTo(Value2.Length);
if (Result == 0)
{
Result = Value1.CompareTo(Value2);
}
return Result;
}
Using this to answer the OP's question:
if (HexStringCompare(a, b) > 0)
MessageBox.Show("a is greater");
Related
What is the best way to convert a string of digits into their equivalent ASCII characters?
I think that I am over-complicating this.
Console.WriteLine($"Enter the word to decrypt: ");
//store the values to convert into a string
string vWord = Console.ReadLine();
for (int i = 0; i < vWord.Length; i++)
{
int convertedIndex = vWord[i];
char character = (char)convertedIndex;
finalValue += character.ToString();
Console.WriteLine($"Input: {vWord[i]} Index: {convertedIndex} Char {character}");
}
If the expected input values are something like this: 65 66 67 97 98 99, you could just split the input and cast the converted int values to char:
string vWord = "65 66 67 97 98 99";
string result = string.Join("", vWord.Split().Select(n => (char)(int.Parse(n))));
Console.WriteLine($"Result string: {result}");
This method, however, doesn't perform any error checking on the input string. When dealing with user input, this is not a great idea. We better use int.TryParse() to validate the input parts:
var result = new StringBuilder();
var ASCIIValues = vWord.Split();
foreach (string CharValue in ASCIIValues) {
if (int.TryParse(CharValue, out int n) && n < 127) {
result.Append((char)n);
}
else {
Console.WriteLine($"{CharValue} is not a vaid input");
break;
}
}
Console.WriteLine($"Result string: {result.ToString()}");
You could also use the Encoding.ASCII.GetString method to convert to string the Byte array generated by the byte.Parse method. For example, using LINQ's Select:
string vWord = "65 66 67 97 98 267";
try
{
var CharArray = vWord.Split().Select(n => byte.Parse(n)).ToArray();
string result = Encoding.ASCII.GetString(CharArray);
Console.WriteLine($"String result: {result}");
}
catch (Exception)
{
Console.WriteLine("Not a vaid input");
}
This will print "Not a vaid input", because one of the value is > 255.
Should you decide to allow an input string composed of contiguous values:
651016667979899112101 => "AeBCabcpe"
You could adopt this variation:
string vWord2 = "11065666797989911210110177";
int step = 2;
var result2 = new StringBuilder();
for (int i = 0; i < vWord2.Length; i += step)
{
if (int.TryParse(vWord2.Substring(i, step), out int n) && n < 127)
{
if (n <= 12 & i == 0) {
i = -3; step = 3; ;
}
else if(n <= 12 & i >= 2) {
step = 3; i -= step;
}
else {
result2.Append((char)n);
if (step == 3) ++i;
step = 2;
}
}
else {
Console.WriteLine($"{vWord2.Substring(i, step)} is not a vaid input");
break;
}
}
Console.WriteLine($"Result string: {result2.ToString()}");
Result string: nABCabcpeeM
As Tom Blodget requested, a note about the automatic conversion
between ASCII characters-set and Unicode CodePoints.
This code produces some ASCII characters using an integer value, corresponding to the character in the ASCII table, casting the value to a char type and converting the result to a Windows standard Unicode (UTF-16LE) string.
Why there's no need to explicitly convert the ASCII chars to their Unicode representation?
Because, for historical reasons, the lower Unicode CodePoints directly map to the standard ASCII table (the US-ASCII table).
Hence, no conversion is required, or it can be considered implicit.
But, since the .Net string type uses UTF-16LE Unicode internally (which uses a 16-bit unit for each character in the lower Plane, two 16-bit code units for CodePoints greater or equal to 216), the memory allocation in bytes for the string is double the number of characters.
In the .Net Reference Source, StringBuilder.ToString() will call the internal wstrcpy method:
wstrcpy(char *dmem, char *smem, int charCount)
which will then call Buffer.Memcpy:
Buffer.Memcpy((byte*)dmem, (byte*)smem, charCount * 2);
where the size in bytes is set to charCount * 2.
Since the first draft, in the '80s (when the first Universal Character Set (UCS) was developed), one of the primary objectives of the IEEE and the Unicode Consortium (the two main entities that were developing the standard) was to preserve the compatibility with the pre-existing 256 character-set widely used at the time.
Preserving the CodePoints definition, thus preserving compatibility over time, is a strict rule in the Unicode world. This concept and rules apply to all modern variable length Unicode encodings (UTF-8, UTF-16, UTF-16LE, UTF-32 etc.) and to all CodePoints in the Basic Multilingual Plane (CodePoints in the ranges U+0000 to U+D7FF and U+E000 to U+FFFF).
On the other hand, there's no explicit guarantee that the same Local CodePage encoding (often referred to as ANSI Encoding) will produce the same result in two machines, even when the same System (and System version) is in use.
Some other notes about Localization and the Unicode Common Locale Data Repository (CLDR)
You can break the problem down into two parts:
P1. You want to take a string input of space-separated numbers, and convert them to int values:
private static int[] NumbersFromString(string input)
{
var parts = input.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
var values = new List<int>(parts.Length);
foreach (var part in parts)
{
int value;
if (!int.TryParse(part, out value))
{
throw new ArgumentException("One or more values in the input string are invalid.", "input");
}
values.Add(value);
}
return values.ToArray();
}
P2. You want to convert those numbers into character representations:
private static string AsciiCodesToString(int[] inputValues)
{
var builder = new StringBuilder();
foreach (var value in inputValues)
{
builder.Append((char)value);
}
return builder.ToString();
}
You can then call it something like this:
Console.WriteLine(AsciiCodesToString(NumbersFromString(input)));
Try it online
I need to generate unique strings starting from int number (id). The length must be proportionally incremental, so for tiny ids I have to generate unique strings of four characters. For big ids I have to generate strings much more complex, with growing size when needed (max 8 digit) in order to accomplish the uniqueness.
All this procedure must be done with two opposite functions:
from id -> obtain string
from string -> obtain id
Unique strings must be composed by numbers and characters of a specific set (379CDEFHKJLMNPQRTUWXY)
Is there a well know algorithm to do this? I need to do this in c# or better in tsql. Ideas are also appreciated.
Edit
I have "simply" the need to encode (and than decode) the number. I've implemented this routines for my alphabet (21 symbols length):
Encode:
public static String BfEncode(long input)
{
if (input < 0) throw new ArgumentOutOfRangeException("input", input, "input cannot be negative");
char[] clistarr = BfCharList.ToCharArray();
var result = new Stack<char>();
while (input != 0)
{
result.Push(clistarr[input % 21]);
input /= 21;
}
return new string(result.ToArray());
}
And decode:
public static Int64 BfDecode(string input)
{
var reversed = input.ToLower().Reverse();
long result = 0;
int pos = 0;
foreach (char c in reversed)
{
result += BfCharList.IndexOf(c.ToString().ToUpper()) * (long)Math.Pow(21, pos);
pos++;
}
return result;
}
I've generated example strings in a loop starting from 10000 to 10000000 (!). Starting from 10K I can generate strings of 4 digits length. After the generation I've put all the strings in a list and checked for uniqueness (I've done it with parallel foreach...). At the number 122291 the routine thrown an exception because there is a duplicate! Is it possibile?
The base conversion to a custom alphabet is not a good solution?
I need to travers the string ,which should be the string of digits and make some arithmetic operations on these digits
for (int i = data.Length - 1; i >= 0; --i)
{
uint curDigit;
//Convert to uint the current rightmost digit, if convert fails return false (the valid data should be numeric)
try
{
curDigit = Convert.ToUInt32(data[i]);
//arithmetic ops...
}
catch
{
return false;
}
I test it with the following input data string.
"4000080706200002"
For i = 15,corresponding to the rightmost digit 2,I get 50 as an output from
curDigit = Convert.ToUInt32(data[i]);
Can someone please explain me what is wrong?and how to correct the issue
50 is the ascii code for '2'. what you need is '2' - '0' (50-48)
byte[] digits = "4000080706200002".Select(x => (byte)(x - '0')).ToArray();
http://www.asciitable.com/
What you are getting back is the ascii value of character 2, You can use call ToString on the character item and then call Convert.ToUnit32, Consider the example:
char x = '2';
uint curDigit = Convert.ToUInt32(x.ToString());
this will give you back 2 as curDigit
For your code you can just use:
curDigit = Convert.ToUInt32(data[i].ToString());
Another option is to use char.GetNumericValue like:
uint curDigit = (UInt32) char.GetNumericValue(data[i]);
char.GetNumericValue returns double and you can cast the result back to UInt32
The problem is that data[i] returns a char variable, that essentialy is an integer holding the ASCII code of the character. So '2' corresponds to 50.
There are 2 things you can do to overcome this behaviour:
Better curDigit = Convert.ToUInt32(data[i] - '0'); //Substract the ASCII representation of '0' from the char
curDigit = Convert.ToUInt32(data.Substring(i,1)); //Use substring to return a string instead of char. Note, that this method is less efficient, as Convert from string essentially splits the string into chars, and substract '0' from each and every one of them.
Your getting the ASCII (or Unicode) values for those characters. The problem is that the code points for the characters '0' … '9' are not 0 … 9, but 48 … 57. To fix this, you need to adjust by that offset. For example:
curDigit = Convert.ToUInt32(data[i] - '0');
Or
curDigit = Convert.ToUInt32(data[i] - 48);
Rather than messing around with ASCII calculations you could use UInt32.TryParse as an alternative solution. However, this method requires a string input not char, so you would have to modify your approach a little:
string input = "4000080706200002";
string[] digits = input.Select(x => x.ToString()).ToArray();
foreach(string digit in digits)
{
uint curDigit = 0;
if(UInt32.TryParse(digit, out curDigit))
{
//arithmetic ops...
}
//else failed to parse
}
What is the best way to compare two hexadecimal numbers (that is a string)? For instance,
string a = "3F";
string b = "32";
if (a > b)
MessageBox.Show("a is greater");
Should work. (Assuming > has been properly overloaded).
You can always convert them to ints and compare them that way:
int a = int.Parse("3E", System.Globalization.NumberStyles.HexNumber);
int b = int.Parse("32", System.Globalization.NumberStyles.HexNumber);
if (a > b)
MessageBox.Show("a is greater");
Seems safer :)
Convert them to integers and compare the integers.
There is also a simple algo based on String comparisson:
Assumed your numbers have a unique format: always lower case or higher case letters. Leading 0x or not, no leading zeros. Then you can do like this:
If number a has more digits than number b: a > b
If the number of digits is equal you could use String.Compare.
This algo has the advantage it is not limited to 32 or 64 bits.
Here is a fairly robust implementation of hendrik’s suggestion. There are a number of ways it could be optimized if your input strings have known attributes, but it should be able to compare valid hex strings of any size and/or with mixed formats.
public int HexStringCompare(string value1, string value2)
{
string InvalidHexExp = #"[^\dabcdef]";
string HexPaddingExp = #"^(0x)?0*";
//Remove whitespace, "0x" prefix if present, and leading zeros.
//Also make all characters lower case.
string Value1 = Regex.Replace(value1.Trim().ToLower(), HexPaddingExp, "");
string Value2 = Regex.Replace(value2.Trim().ToLower(), HexPaddingExp, "");
//validate that values contain only hex characters
if (Regex.IsMatch(Value1, InvalidHexExp))
{
throw new ArgumentOutOfRangeException("Value1 is not a hex string");
}
if (Regex.IsMatch(Value2, InvalidHexExp))
{
throw new ArgumentOutOfRangeException("Value2 is not a hex string");
}
int Result = Value1.Length.CompareTo(Value2.Length);
if (Result == 0)
{
Result = Value1.CompareTo(Value2);
}
return Result;
}
Using this to answer the OP's question:
if (HexStringCompare(a, b) > 0)
MessageBox.Show("a is greater");
The number is bigger than int & long but can be accomodated in Decimal. But the normal ToString or Convert methods don't work on Decimal.
I believe this will produce the right results where it returns anything, but may reject valid integers. I dare say that can be worked around with a bit of effort though... (Oh, and it will also fail for negative numbers at the moment.)
static string ConvertToHex(decimal d)
{
int[] bits = decimal.GetBits(d);
if (bits[3] != 0) // Sign and exponent
{
throw new ArgumentException();
}
return string.Format("{0:x8}{1:x8}{2:x8}",
(uint)bits[2], (uint)bits[1], (uint)bits[0]);
}
Do it manually!
http://www.permadi.com/tutorial/numDecToHex/
I've got to agree with James - do it manually - but don't use base-16. Use base 2^32, and print 8 hex digits at a time.
I guess one option would be to keep taking chunks off it, and converting individual chunks? A bit of mod/division etc, converting individual fragments...
So: what hex value do you expect?
Here's two approaches... one uses the binary structure of decimal; one does it manually. In reality, you might want to have a test: if bits[3] is zero, do it the quick way, otherwise do it manually.
decimal d = 588063595292424954445828M;
int[] bits = decimal.GetBits(d);
if (bits[3] != 0) throw new InvalidOperationException("Only +ve integers supported!");
string s = Convert.ToString(bits[2], 16).PadLeft(8,'0') // high
+ Convert.ToString(bits[1], 16).PadLeft(8, '0') // middle
+ Convert.ToString(bits[0], 16).PadLeft(8, '0'); // low
Console.WriteLine(s);
/* or Jon's much tidier: string.Format("{0:x8}{1:x8}{2:x8}",
(uint)bits[2], (uint)bits[1], (uint)bits[0]); */
const decimal chunk = (decimal)(1 << 16);
StringBuilder sb = new StringBuilder();
while (d > 0)
{
int fragment = (int) (d % chunk);
sb.Insert(0, Convert.ToString(fragment, 16).PadLeft(4, '0'));
d -= fragment;
d /= chunk;
}
Console.WriteLine(sb);