C# : How can I encode GUIDs to 11 character ids? - c#

Like https://www.youtube.com/watch?v={id}
{id}:11characters
I try to use Convert.ToBase64String
string encoded = Convert.ToBase64String(guid.ToByteArray())
.Replace("/", "_")
.Replace("+", "-").Replace("=", "");
like this
The GUIDs is only reduced to 22 characters.
How can I encode GUIDs to 11 character ids?(or less 22 characters)

11 characters, even assuming you could use all 8 bits per character in a URL (hint: you can't), would only allow 88 bits.
A UUID/GUID is 128 bits. Therefore, the conversion you propose is not possible without losing data.

This is off topic answer but it might give you an ID with only 11 characters.
In C# a long value has 64 bits, which if encoded with Base64, there will be 12 characters, including 1 padding =. If we trim the padding =, there will be 11 characters.
One crazy idea here is we could use a combination of Unix Epoch and a counter for one epoch value to form a long value. The Unix Epoch in C# DateTimeOffset.ToUnixEpochMilliseconds is in long format, but the first 2 bytes of the 8 bytes are always 0, because otherwise the date time value will be greater than the maximum date time value. So that gives us 2 bytes to place an ushort counter in.
So, in total, as long as the number of ID generation does not exceed 65536 per millisecond, we can have an unique ID:
// This is the counter for current epoch. Counter should reset in next millisecond
ushort currentCounter = 123;
var epoch = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
// Because epoch is 64bit long, so we should have 8 bytes
var epochBytes = BitConverter.GetBytes(epoch);
if (BitConverter.IsLittleEndian)
{
// Use big endian
epochBytes = epochBytes.Reverse().ToArray();
}
// The first two bytes are always 0, because if not, the DateTime.UtcNow is greater
// than DateTime.Max, which is not possible
var counterBytes = BitConverter.GetBytes(currentCounter);
if (BitConverter.IsLittleEndian)
{
// Use big endian
counterBytes = counterBytes.Reverse().ToArray();
}
// Copy counter bytes to the first 2 bytes of the epoch bytes
Array.Copy(counterBytes, 0, epochBytes, 0, 2);
// Encode the byte array and trim padding '='
var shortUid = Convert.ToBase64String(epochBytes).TrimEnd('=');

Related

Algorithm to get ushort number from GUID c#

How to get an unique(most of the time) ushort number from GUID, I have tried below code but since I am converting it to ushort so it is just ignoring the LSB hexadecimal values of GUID
static ushort GetId() {
Guid guid = Guid.NewGuid();
byte[] buffer = guid.ToByteArray();
return BitConverter.ToUInt16(buffer, 0);
}
FYI: Somewhere in my code, I have a guid and I want to keep the corresponding ushort number.
I have tried below code but since I am converting it to ushort so it
is just ignoring the LSB hexadecimal values of GUID
Yes, this is correct and for good reason, you cannot store 128 bits of data in 16 bits of data.
Name Length (bytes) Contents
---------------------------------------------------------------------------------------
time_low 4 integer giving the low 32 bits of the time
time_mid 2 integer giving the middle 16 bits of the time
time_hi_and_version 2 4-bit "version" in the most significant bits, followed by the high 12 bits of the time
clock_seq_hi_and_res clock_seq_low 2 1-3 bit "variant" in the most significant bits, followed by the 13-15 bit clock sequence
node 6 the 48-bit node id
If you want the last 16 bits (2 bytes, 4 hex values) Just reverse the array
Array.Reverse(buffer, 0, buffer.Length);
return BitConverter.ToUInt16(buffer, 0);
Note what you are doing is very suspect, and i truly think you need to rethink your design

Converting string to sortable number

Is there any way of converting a string value (any content) to a number such that they both sort in the same way? I don't need to be able to convert the number back to a string. In fact it would be an advantage if it were impossible to convert it back.
I don't need to be able to sort any length of string. If a 64-bit long integer is used as the sort-value then I could trim the texts to a value that fits this number range.
I don't think there can be 100% correct way since sorting a string depends on the culture. For ex
int c1 = String.Compare("AA", "BB", false, CultureInfo.GetCultureInfo("en-US")); //return -1
int c2 = String.Compare("AA", "BB", false, CultureInfo.GetCultureInfo("da-DK")); //return 1
The closest thing I can think of is:
ulong l = BitConverter.ToUInt64(Encoding.UTF8.GetBytes(str), 0);
PS: pad str if its len is shorter than 8
You could take the first 8 bytes from the string, the 8 bytes would make up a ulong. It would only be 4 characters of the string with unicode, or 8 characters if you limit the strings to ASCII.

Serial reading, only first 128 values taken into account

I am reading a line from MCU via serial port. The line consists in 14 characters terminated by "OK". The characters are converted to int then processed. The problem is when the value becomes larger than 128. For values larger than 128 the value (int converted) remains at 63. Here is the code:
serialPort1.DiscardInBuffer();
serialPort1.DiscardOutBuffer();
serialPort1.Write("d");//request line from mcu
Thread.Sleep(100);
string line = serialPort1.ReadLine();
int p1_low = line[0];
int p1_high = line[1]*256;
int p1 = p1_low + (p1_high);
label1.Text = "Input Sensor: " + p1_low;
p1_low varies much often than p1_high and sticks to 63 value when is larger than 128. Where can be the problem?
Change the encoding to
SerialPort1.Encoding = System.Text.Encoding.GetEncoding(28591)
The default encoding, as you have discovered, replaces byte values > 127 with a '?'. Encoding 28591 preserves byte values greater than 127.
You do not need the thread sleep as .ReadLine blocks.
It sounds like you are set to use 7 data bits. Change the data bits config value to 8, so you can get all 256 values.
Reference: http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.databits.aspx
Use the method
Write(Byte[], int32, int32) because a byte is a numerical value --> (0-255) or (0x00-0xFF) or (b00000000-11111111). A char or a string can be encoded but not a byte.
Make sure to
NOT use Write(Char[], int32, int32)

Way to generate a unique number that does not repeat in a reasonable time?

I'm integrating/testing with a remote web service and even though it's the "QA" endpoint, it still enforces a unique email address on every call.
I can think of DateTime.Now.Ticks (e.g. 634970372342724417) and Guid.NewGuid(), but neither of those can be coalesced into an email with max. 20 chars (or can they?).
I suppose it's not that hard to write out to a file a number that contains the last number used and then use email1#x.com, email2#x.com, etc... but if I can avoid persisting state I always do.
Does anyone have a trick or an algorithm that gives something of a short length "guid" that is unique to a reasonably long time period (say a year) that I could use for my email addresses of max length 20 chars with (max length of guid) = 14 = 20 - length of "#x.com"?
If you assume that you will not generate two e-mail addresses at the same 'tick', then you can indeed use the ticks to generate an e-mail address.
However, if ticks is a 64-bit number, and you write out that number, you will end up with more than 20 characters.
The trick is to encode your 64-bit number using a different scheme.
Assume that you can use the 26 characters from the western alphabet + 10 digits. This makes 36 possible characters. If you take 5 bits, you can represent 32 characters. That should be enough.
Take the 64-bits and divide them in groups of 5 bits (64 /5 is about 13 groups). Translate every 5 bits to one character. That way you end up with 13 characters, and you can still add a character in front of it).
long ticks = DateTime.Now.Ticks;
byte[] bytes = BitConverter.GetBytes(ticks);
string id = Convert.ToBase64String(bytes)
.Replace('+', '_')
.Replace('/', '-')
.TrimEnd('=');
Console.WriteLine (id);
Yields:
Gq1rNzbezwg
If you get the following digits from your date-time, you should be able to make it work...
Soemthing like:
DateTime.Now.ToString("yyMMddHHmmssff");
which is 16 characters, leaving 4 for some other prefix as you need.
So, Feb 21, 2013, at approximately 10:21 would be "130321102142" and the next one would be "130321102169", etc...
Have a look at http://msdn.microsoft.com/en-us/library/zdtaw1bw.aspx for more details on datetime formatting.
Since you specified at least 1 second between each call, this should work :
DateTime.Now.ToString("yyyyMMddHHmmss");
its exactly 14 characters.
Just to add... If you want to use number only from ticks, you can by using substring, for example:
int onlyThisAmount = 20;
string ticks = DateTime.Now.Ticks.ToString();
ticks = ticks.Substring(ticks.Length - onlyThisAmount);
/// <summary>
/// Get a unique reference number.
/// </summary>
/// <returns></returns>
public string GetUniqueReferenceNumber(char firstChar)
{
var ticks = DateTime.Now.Ticks;
var ticksString = ticks.ToString();
var ticksSubString = ticksString.Substring((ticksString.Length - 15 > 0) ? ticksString.Length - 15 : 0);
if (this.currentTicks.Equals(ticks))
{
this.currentReference++;
if (this.currentReference >= 9999)
{
// Only when there are very fast computers.
System.Threading.Thread.Sleep(1);
}
return (firstChar + ticksSubString + this.currentReference.ToString("D4")).PadRight(20, '9');
}
this.currentReference = -1;
this.currentTicks = ticks;
return (firstChar + ticksSubString).PadRight(20, '9');
}
In my case I needed to create a unique reference number with a unique first character and a maximum of 20 characters. Maybe you can use the function below, it allows you to create 9999 unique numbers within one tick. (zero included)
Of course you can create your own implementation without the first character and maximum character count of 20
public async Task<string> GeneratePatientNumberAsync()
{
var random = new Random();
var chars = DateTime.Now.Ticks + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789" + DateTime.Now.Ticks;
return new string(Enumerable.Repeat(chars, 5)
.Select(s => s[random.Next(s.Length)]).ToArray());
}

How to create byte[] with length 16 using FromBase64String [duplicate]

This question already has an answer here:
Calculate actual data size from Base64 encoded string length
(1 answer)
Closed 10 years ago.
I have a requirement to create a byte[] with length 16. (A byte array that has 128 bit to be used as Key in AES encryption).
Following is a valid string
"AAECAwQFBgcICQoLDA0ODw=="
What is the algorithm that determines whether a string will be 128 bit? Or is trial and error the only way to create such 128 bit strings?
CODE
static void Main(string[] args)
{
string firstString = "AAECAwQFBgcICQoLDA0ODw=="; //String Length = 24
string secondString = "ABCDEFGHIJKLMNOPQRSTUVWX"; //String Length = 24
int test = secondString.Length;
byte[] firstByteArray = Convert.FromBase64String((firstString));
byte[] secondByteArray = Convert.FromBase64String((secondString));
int firstLength = firstByteArray.Length;
int secondLength = secondByteArray.Length;
Console.WriteLine("First Length: " + firstLength.ToString());
Console.WriteLine("Second Length: " + secondLength.ToString());
Console.ReadLine();
}
Findings:
For 256 bit, we need 256/6 = 42.66 chars. That is rounded to 43 char. [To make it divisible by 4 add =]
For 512 bit, we need 512/6 = 85.33 chars. That is rounded to 86 char. [To make it divisible by 4 add ==]
For 128 bit, we need 128/6 = 21.33 chars. That is rounded to 22 char. [To make it divisible by 4 add ==]
A base64 string for 16 bytes will always be 24 characters and have == at the end, as padding.
(At least when it's decodable using the .NET method. The padding is not always inlcuded in all uses of base64 strings, but the .NET implementation requires it.)
In Base64 encoding '=' is a special symbol that is added to end of the Base64 string to indicate that there is no data for these chars in original value.
Each char is equal to 6 original bits of data, so to produce 8 bit values the string length has to be dividable by 4 without remainder. (6 bits * 4 = 8 bits * 3). When the resulting BASE64 string is shorter than 4n then '=' are added at the end to make it valid.
Update
Last char before '==' encodes only 2 bits of information, so by replacing it with all possible Base64 chars will give you only 4 different keys out of 64 possible combinations. In other words, by generating strings in format "bbbbbbbbbbbbbbbbbbbbbb==" (where 'b' is valid Base64 character) you'll get 15 duplicate keys per each unique key.
You can use PadRight() to pad the string to the end of it with a char that you will later remove once decrypted.

Categories