We were unable to load Disqus. If you are a moderator please see our troubleshooting guide.
Actually, you can't transform (lowercase, uppercase) the characters on a `ShortGuid`, because they're essentially base64, just with well known special characters replaced (/, +) or removed (==). Base64 needs the entire range of chars to correctly represent the data within (https://en.wikipedia.org/wi...
Running the lowercase on the `ShortGuid` as you've shown will result in a different byte array when `Decode` calls `Convert.FromBase64String()`
Glad you've found it helpful, Bret!
Dang, that's an interesting one though - thanks for letting me know - I'll write some unit tests for it and come back to you soon.
Nice piece of work. The only thing I would change on it, is to make the ShortGuid an immutable struct. That being, remove the setters for the properties.
Structs ideally represent values that don't change. It is easy enough to create a new instance of ShortGuid, you don't really need to change the value of it.
You should put this on GitHub, and make a NuGet package.
Hey Dave, could you make this a NuGet package? I'd love to use it, but my project wouldn't allow me to use this code snippet due to possible maintenance that could come up. I think this would be awesome if you could install it as a package, even though it is very simple!
I finally got around to doing this chriszo111 - my, how the years fly [post updated with details if you're still interested]
Hi,
very nice piece of code. I have a problem. When deserializing shortguid and string value is null Decode method fail. It may stay null as like as never assigned. Here is update:
public static Guid Decode(string value)
{
if (string.IsNullOrEmpty(value))
return new Guid();
value = value
.Replace("_", "/")
.Replace("-", "+");
byte[] buffer = Convert.FromBase64String(value + "==");
return new Guid(buffer);
}
How to make same code for VB.net?
The strings are not unique, it seems. I noticed to following strings (off by 1) actually result in the same Guid which makes me nervous...
mgD4GvCOZE2MtZWw1vIqvr
mgD4GvCOZE2MtZWw1vIqvg
Indeed, they produce the same byte array when appending "==" and running through `Convert.FromBase64String()`. I would expect the *same input* to produce a *consistent output* in this case.
I'm curious, can you share some code that reproduces this dasjestyr?
Seems pretty consistent. I just genated a new guid, made a short guid, changed the last character, made a new short guid from it and compared to the old guid and they were equal but shouldn't be.
Okay, that makes more sense that it's been tempered with.
If you step through a base64 decoder, you'll see the maths for this. The v (47) is shifted left, yielding 188, and the r (32) and g (43) are shifted right, yielding 2 - giving the same byte (190) as the last byte. The remainder is used if the base64 string is longer.
You won't get clashes. Anyone using base64 to encode the guid will yield the same string.
This seems like a huge implementation for a simple problem. We can just use:
var shortUid = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Trim('=');
There's a spec for UrlEncodedBase64 that you'd wanna use
If you want URL compatibility, you'll also want to replace plus and slash characters as per Mad's original En/Decode methods (which aren't huge at all), that's all you need.
But sure, this has more code, mainly aimed around convenience and casting between types.
I find it handy to mark variables as this type, which to me indicates a differing incoming/outgoing value (a fudged string) verses the value I want to use (the guid).
I ran into this issue that Nicolas mentioned 4 years ago "Unfortunately it won't work with lower case only URLs (often used to optimize SEO)." I think maybe the solutions is to use Base32Encoding instead. I've been working out some tests with the code Shane posted here http://stackoverflow.com/qu.... The resulting strings aren't quite as short but they do work.
f35cfe5c-1c8a-4e17-be7f-b37196538eaf = "lt7fz44kdqlu5pt7wnyzmu4ov4"
public static string Encode ( Guid guid ) {
var encoded = Base32Encoding.ToString( guid.ToByteArray() );
return encoded.Substring( 0, 26 ).ToLower();
}
public static Guid Decode ( string value ) {
try {
var buffer = Base32Encoding.ToBytes( value.ToUpper() + "======" );
return new Guid( buffer );
} catch {
return Guid.Empty;
}
}
Thanks for sharing.
Thanks for sharing Dave! This will come in real handy for a project I'm doing.
Is it ok to use this code in a commercial work?
Hi Albert - yes indeed!
I've intended to add a license for sometime now - I'll update my blog to use the MIT (e.g. https://github.com/csharpvi...
Thanks for reminding me.
Just thought I'd share a SQL function that converts a UNIQUEIDENTIFIER field to the ShortGuid string representation. I derived this using this C# library so thanks to the author:
/****** Object: UserDefinedFunction [dbo].[GetShortId] Script Date: 7/15/2015 1:08:45 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[GetShortId] ( @Input UNIQUEIDENTIFIER )
RETURNS VARCHAR(50)
BEGIN
DECLARE @InputBinary VARBINARY(MAX)
SET @InputBinary = CAST(@Input AS VARBINARY(MAX))
DECLARE @EncodedGuid VARCHAR(38)
SET @EncodedGuid = CAST(N'' as xml).value('xs:base64Binary(sql:variable("@InputBinary"))', 'VARCHAR(max)')
DECLARE @StrippedGuid VARCHAR(32)
SET @StrippedGuid = REPLACE(REPLACE(@EncodedGuid, '/', '_'), '+', '-')
RETURN SUBSTRING(@StrippedGuid, 1, 22) -- Removes the '==' from the end
END
GO
Thanks Steven - that's handy.
I also ended up writing the converse to the above function to convert a ShortID back to a uniqueidentifier. Here it is for anyone that needs it:
/****** Object: UserDefinedFunction [dbo].[ConvertFromShortId] Script Date: 7/27/2015 10:30:29 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[ConvertFromShortId] ( @Input NVARCHAR(22) )
RETURNS UNIQUEIDENTIFIER
BEGIN
DECLARE @InputConverted NVARCHAR(24)
SET @InputConverted = REPLACE(REPLACE(@Input, '_', '/'), '-', '+') + '=='
DECLARE @Decoded varbinary(max)
set @Decoded = cast('' as xml).value('xs:base64Binary(sql:variable("@InputConverted"))', 'varbinary(max)')
RETURN CAST(@decoded AS uniqueidentifier)
END
GO
Good job! Thanks a million. :)
Hi Dave,
Your code convinced me to switch my standard code for token generation to use ShortGuids.
Saved me time and it works well.
Thanks for sharing this!
Regards,
Rob
Updated link to Mads Kristensen's post:
http://madskristensen.net/p...
Great stuff. Been hacking my own, but this is much better than my efforts. Much appreciated.
new links are not working either... i dont know if its me
Mads has changed his domain so the links on this article are wrong now.
Please change Mads Kristensen's links to :
http://madskristensen.net/p...
How to convert to shortGUID without hyphen?
Hi,
Nice post. But I am looking for short unique string Maximum of 10 alpha numeric characters. the above post generate unique but length is more. Could you please suggest any post you have come across for the same?
Regards
Nikhil
@Thiago Lemos:
The problem is that is doesn't _look_ as well structured as a Guid with groups and separators
e.g. 00000000-0000-0000-0000-000000000000
It has the following consistency properties.
1. it's 22 characters long
2. the characters can be any of A-Z, a-z, 0-9, a dash (-) and an underscore (_)
So a regex should use the following pattern to match exactly.
new Regex(@"^[A-Za-z0-9-_]{22}$")
I haven't actually tried it in a routing environment as yet. Might have a look at see the best way to handle this.
Remember you can pass a string (or a Guid) to ShortGuid and it will implicitly convert it.
Is possible to match this shortguid with a regex? I'm asking this because I want use ShortGuid in my URLs, but i need the regex to define the routes in my MVC project! original GUID has a pattern who give me a way to validate, thanks for share this code, this is very helpful
@Boris: It depends.
When comparing a ShortGuid to another ShortGuid, the underlying Guid is used to compared. So case doesn't matter.
When comparing a string or Guid to a ShortGuid, the string (or Guid) will be implicitly converted to a ShortGuid, then the underlying Guids will be compared as above.
If you've compare a string representation of a ShortGuid to something that is _not_ a ShortGuid, case will definitively matter.
HTH
Thanks everyone else for the kind words and contributions.
Hi Dave,
Very good work.
Just a question about the short GUID comparisons. Is the string representation of the short GUID case insensitive (as it is for the normal GUID)?
Hi Dave,
Thanks a lot for this! It's really helpful for something that I am working on currently. :)
/Najeeb/
Unfortunately it won't work with lower case only URLs (often used to optimize SEO).
Will definitely use it . Thnx a lot for this clever lib!
Cheers from Greece
in case you need just binder:
public class ShortGuidModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue;
var isShort = value.Length == 22;
var shortGuidValue = isShort ? new ShortGuid(value) : new ShortGuid(Guid.Parse(value));
if (bindingContext.ModelType == typeof(ShortGuid))
return shortGuidValue.Value;
if (bindingContext.ModelType == typeof(Guid))
return shortGuidValue.Guid;
throw new AnelosophyException("can handle guids only");
}
}
I would chage the property setters from:
if (value != _guid)
{
_guid = value;
_value = Encode(value);
}
if (value != _value)
{
_value = value;
_guid = Decode(value);
}
to:
if (value != _guid)
{
_value = Encode(value);
_guid = value;
}
if (value != _value)
{
_guid = Decode(value);
_value = value;
}
to avoid corrupting object state in case Encode or Decode would throw an exception.
Thanks for the code.
If I were you, I would recommend my code to Microsoft, to include in future versions of .NET.
Once we wanted to create random coupons for our products, and we wished for such a utility to produce shorter random strings. But we couldn't find anything, and the .NET's Guid was inefficient (too long) in our case.
Great thought buddy. Thanks.
Thanks very much for this, it has come in very handy.
I added a TryParse method for my purposes:
public static bool TryParse(string input, out ShortGuid result)
{
try
{
result = new ShortGuid(input);
}
catch (Exception)
{
result = ShortGuid.Empty;
}
return (result != ShortGuid.Empty);
}
I'm being lazy and relying on an exception from the constructor so I'm sure there are better ways (also it would treat an empty ShortGuid as invalid) - but it works for me :)
Thanks again
@Dave,
Thanks. I'm in a highly regulated industry right now and the lawyers tend to bristle at code without an explicit license. I'll deal with them though.
Thanks again!
@Mike: Thanks, you raise an interesting point - I don't currently publish a licence under which snippets and work on this blog can be re-used.
I've intended it to be freely available, customisable and distributable.
I'll choose a license shortly, however, please feel free to use it as you will until I've specified a license.
HTH
I don't see anywhere that you've specified a license for this work. Would you consider releasing it under the MIT license? Thanks.
Thank you! This is great stuff!
@Kieran: Appreciate the feedback - though I'm unsure why you'd think that provides a gain in efficiency. It's more of a change in coding style isn't it?
If you look at the IL generated between your version and mine, the only difference is "equality" verses "inequality".
IL for original version:
L_0008: call bool [mscorlib]System.Guid::op_Inequality(valuetype [mscorlib]System.Guid, valuetype [mscorlib]System.Guid)
IL for your version:
L_0008: call bool [mscorlib]System.Guid::op_Equality(valuetype [mscorlib]System.Guid, valuetype [mscorlib]System.Guid)
Even decompiling your method (using Reflector, ILSpy may give a different result) to C# it turns out like this:
if (!(value == this._guid))
{
this._guid = value;
this._value = Encode(value);
}
Thanks for sharing this, very useful.
If I could suggest a little refactoring on two properties just for a small efficency gain.
Setters for Guid and Value:
public Guid Guid
{
get { return _guid; }
set
{
// Code change
if (value == _guid) return;
_guid = value;
_value = Encode(value);
}
}
public string Value
{
get { return _value; }
set
{
// Code change
if (value == _value) return;
_value = value;
_guid = Decode(value);
}
}
Thanks again
Hey Dave, love this - especially coming from a kiwi(I presume from the domain?) I use this regularly and have done so for a long time. I hope one day i'll get the chance to buy you a beer or two in real life. I've just come across a situation where I need something like this to be all lower case but
ShortGuid.Decode(ShortGuid.Encode(b5b35c4d-2b29-4773-b8ba-18ccab5e5bc1).ToLowercase())=b6b3fcb6-2bc9-4773-b8ba-18e6ab5e5bc2... i.e b5b35c4d-2b29-4773-b8ba-18ccab5e5bc1= b6b3fcb6-2bc9-4773-b8ba-18e6ab5e5bc2 (2 char difference).
Any thoughts on how this might be possible?