I'm personally tired of reimplementing "a ticket" mechanism for my web projects for some one-time operations like account activation or password reset. I know it's simple but it requires me to keep (and persist!) two pieces of data: a ticket itself and an expiration date.
So I have this idea, to generate a unique string with embedded datetime (expiration date) in it, which we can check upon receiving as a part of url-request.
I've started with this:
var clearTicket = Convert.ToInt64(expiration.ToString("yyyyMMddhhmm")).ToString("x12") + key.ToString("N");
But I want it to be more compact. Something BaseXX-ish I suppose.
Any ideas how to implement this encoding/decoding efficiently (considering url-safe charset)?
It took me some time, so I hope it helps:
First of all, you should substract 200000000000 from "yyyyMMddhhmm", because you actually don't need the first two digits for the next 88 years.
Here is the implementation for encoding and decoding Base64, using only URL-safe characters.
If you have any questions, feel free to ask.
public string Base64Encode (Int64 Number)
{
string HelpString = "";
if (Number >= 64)
{
HelpString = Base64Encode(Number / 64);
}
return (HelpString += Base64EncodeHelper(Number % 64));
}
public string Base64EncodeHelper(Int64 Number)
{
string HelpString = "";
Number += 65;
if ((Number >= 65 && Number <= 90) || (Number >= 97 && Number <= 122)) // 0 - 25 and 32 - 57
{
HelpString = Convert.ToString((char)Number);
}
else if (Number >= 91 && Number <= 96) // 26 - 31
{
HelpString = Convert.ToString((char)(Number - 43));
}
else if (Number >= 123 && Number <= 126) // 58 - 61
{
HelpString = Convert.ToString((char)(Number - 69));
}
else if (Number == 127) // 62
{
HelpString = "-";
}
else // 63
{
HelpString = "_";
}
return (HelpString);
}
public Int64 Base64Decode(string Encoded)
{
Int64 Result = 0, HelpInt = 0;
int i = Encoded.Length - 1;
foreach (char Character in Encoded)
{
int CharInInt = (int)Character;
if (Character == '_')
{
HelpInt = 63;
}
else if (Character == '-')
{
HelpInt = 62;
}
else if (((CharInInt + 69) >= 123) && ((CharInInt + 69) <= 126))
{
HelpInt = CharInInt + 4;
}
else if (((CharInInt + 43) >= 91) && ((CharInInt + 43) <= 96))
{
HelpInt = CharInInt - 22;
}
else
{
HelpInt = CharInInt - 65;
}
Result += Convert.ToInt64((Math.Pow(64, Convert.ToDouble(i))) * HelpInt);
i--;
}
return Result;
}
You can extend string like this:
public static string ToURLParameter(this string text)
{
if (String.IsNullOrEmpty(text)) return "";
// to lowercase, trim extra spaces
text = text.Trim();
var len = text.Length;
var sb = new StringBuilder(len);
bool prevdash = false;
char c;
//
text = text.Replace('Å', 'A');
text = text.Replace('å', 'a');
text = text.Replace('Ä', 'A');
text = text.Replace('ä', 'a');
text = text.Replace('Ö', 'O');
text = text.Replace('ö', 'o');
for (int i = 0; i < text.Length; i++)
{
c = text[i];
if (c == ' ' || c == ',' || c == '.' || c == '/' || c == '\\' || c == '-')
{
if (!prevdash)
{
sb.Append('-');
prevdash = true;
}
}
else if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z'))
{
sb.Append(c);
prevdash = false;
}
if (i == 80) break;
}
text = sb.ToString();
// remove trailing dash, if there is one
if (text.EndsWith("-"))
text = text.Substring(0, text.Length - 1);
return text;
}
and then you can use it on any variable that is string like:
string something = "asdfljasdklf";
<%= something.ToUrlParameter() %>
I've finally created a solution using techniques described in these answers:
Compressing big number (or string) to small value
How do you convert Byte Array to Hexadecimal String, and vice versa?
Works great!
Thanks you all for your help!
Related
I want to start by saying I am very new (1 week) into learning C#, so I sincerely apologize if this question is obvious. I do understand the reason for the exception, diverse.Length has to be 0 or greater. However, I am required to have the formula in my code so as to get the numbered-positions of the last 2 characters of an ever changing string (diverse).
Below, are 3 sets of code....
Firstly my working method.
static void Main(string[] args)
{
try
{
// Sample data - inputs 3 ints.
Console.WriteLine(Solution1(6, 1, 1));
Console.WriteLine(Solution1(1, 3, 1));
Console.WriteLine(Solution1(0, 1, 8));
Console.WriteLine(Solution1(5, 2, 4));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
static string Solution1(int A, int B, int C)
{
string a = "a"; // a/b/c are added to string diverse when needed.
string b = "b";
string c = "c";
string diverse = "";
int totalLength = A + B + C; // Length of all 3 arrays
for(int i = 1; i <= totalLength; i++)
{
if (A >= B && A >= C && A > 0) { diverse = diverse + a; A = A - 1; }
if (B >= A && B >= C && B > 0) { diverse = diverse + b; B = B - 1; }
if (C >= A && C >= B && C > 0) { diverse = diverse + c; C = C - 1; }
}
return diverse;
}
What I am trying to do is add an additional check to my code. This check will take the printed letters and check to see if 2 of the same letter has previously been printed. If so, it will not print a 3rd. To do this I made a solution that would find the last 2 characters of the string (as I mentioned above) and compare it to the conditional check in the if statement.
Below is the code with this additional check, that I need to get working...
static string Solution1(int A, int B, int C)
{
string a = "a"; // a/b/c are added to string diverse when needed.
string b = "b";
string c = "c";
string diverse = "";
int totalLength = A + B + C; // Length of all 3 arrays
for (int i = 1; i <= totalLength; i++)
{
// Finds the last 2 characters in the diverse string.
int LastTwoChars = diverse.Length - 2;
string twoCharCheck = diverse.Substring(LastTwoChars, 2);
if (A > 0 && B < 2 && C < 2 && twoCharCheck != "aa")
{
diverse = diverse + a; A = A - 1;
}
if (B > 0 && A < 2 && C < 2 && twoCharCheck != "bb")
{
diverse = diverse + b; B = B - 1;
}
if (C > 0 && B < 2 && A < 2 && twoCharCheck != "cc")
{
diverse = diverse + c; C = C - 1;
}
}
return diverse;
}
You can have the string check only after you have at least 2 characters:
int LastTwoChars = diverse.Length - 2;
string twoCharCheck = LastTwoChars >= 0 ? diverse.Substring(LastTwoChars, 2) : string.Empty; // identical with if (LastTwoChars >= 0) twoCharCheck = diverse.Substring(LastTwoChars, 2); else twoCharCheck = string.Empty
if (A > 0 && B < 2 && C < 2 && (LastTwoChars < 0 || twoCharCheck != "aa")) // check the end only if you have at least 2 chars;
{
...
}
....
As you can see the code gets ugly very fast, so I would propose using a helper method:
// returns the last two chars in a string format, or a default user string
private static string LastTwoCharsOrDefault(string input, string default)
{
var lastTwoCharsIdx = input.Length - 2;
if (lastTwoCharsIdx > 0 )
{
return input.Substring(lastTwoCharsIdx, 2);
}
// we don't have at least 2 chars, so lets just return the default
return default;
}
Then you can change your code like this:
if (A > 0 && B < 2 && C < 2 && string.Equals("aa", LastTwoCharsOrDefault(diverse, "aa"), StringComparison.OrdinalIgnoreCase))
...
You can find more about string comparison here.
If I read your code right, you initialize Diverse as: string diverse = "";
and then execute string twoCharCheck = diverse.Substring(LastTwoChars, 2);
LastTwoChars is going to be -2 the first time through the loop.
I was wondering how am i able to negate away the special character.
For example when i key aaa for my decrypted text and move 3 space behind, it shows it ^^^ instead of www. Is there anything wrong with my coding?
This is all the code that i have inside my c#
static void Main(string[] args)
{
string UserInput = "";
int shift;
Shift OBSHIFT = new Shift();
Console.WriteLine("Welcome to Ceasar Shift Program:");
Console.WriteLine("********************************");
Console.WriteLine("type a string to encrypt:");
UserInput = Console.ReadLine();
Console.WriteLine("How many chars would you like to shift?: :");
shift = int.Parse(Console.ReadLine());
Console.Write("Your encrypted string is: ");
Console.WriteLine(OBSHIFT.Decrypt(UserInput, -shift));
Console.Read();
}
class Shift
{
public string Decrypt(string cipherString, int shift)
{
string userOutput = "";
char[] a = cipherString.ToCharArray();
for (int i = 0; i < cipherString.Length; i++)
{
char c = a[i];
int temp;
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
{
temp = (int)(a[i] + shift);
if ((c >= 'A' && c <= 'Z' && temp > 'Z') || (c >= 'a' && c <= 'z' && temp > 'z'))
temp = temp + 26;
else
temp = (int)(a[i] + (shift));
}
else
temp = c;
userOutput += (char)temp;
}
return userOutput;
}
}
The problem is with this piece of code which does the actual shifting:
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
{
temp = (int)(a[i] + shift);
if ((c >= 'A' && c <= 'Z' && temp > 'Z') || (c >= 'a' && c <= 'z' && temp > 'z'))
temp = temp + 26;
else
temp = (int)(a[i] + (shift));
}
I would suggest restructuring in a few ways:
Make sure your shift is always positive to start with, so you only need to worry about going "past" the end of a particular set of characters
Use two separate branches, one for a-z and one for A-Z
Use a StringBuilder to build your string instead of repeated string concatenation
So for example:
public string ShiftText(string input, int shift)
{
// This makes sure the shift is *always* in the range 0-25.
shift = ((shift % 26) + 26) % 26;
StringBuilder output = new StringBuilder();
foreach (char c in input)
{
if (c >= 'a' && c <= 'z')
{
// We'll sort this later
}
else if (c >= 'A' && c <= 'Z')
{
// We'll sort this later
}
else
{
output.Append(c);
}
}
return output.ToString();
}
Now the bits inside the "sort this later" are simpler to work with. For the a-z part, for example:
int shifted = c + shift;
if (shifted > 'z')
{
shifted -= 26;
}
output.Append((char) shifted);
... and then the same for A-Z, but testing against 'Z' instead of 'z'. Yes, this is effectively duplicate code - but it's only there twice, and I think it's simpler to have that duplication than extract it out.
I would suggest going with this type of code instead:
public class Shift
{
public string Decrypt(string cipherString, int shift)
{
var alphabet =
Enumerable
.Concat(
Enumerable.Range((int)'a', 26),
Enumerable.Range((int)'A', 26))
.Select(i => (char)i)
.ToArray();
var map =
alphabet
.Zip(
alphabet.Concat(alphabet).Concat(alphabet).Skip(alphabet.Length + shift),
(c0, c1) => new { c0, c1 })
.ToDictionary(x => x.c0, x => x.c1);
return String.Join("", cipherString.Select(x => map.ContainsKey(x) ? map[x] : x));
}
}
The three parts are nicely distinct. (1) the alphabet is built. It can contain any number of characters, so long as there are no repeats. (1) the map is simply a dictionary built by mapping the alphabet to the alphabet shifted by shift characters. (3) returns the input cypherString mapped by the map where the key exists.
So we were asked to convert a Hexadecimal Value (stored in a String) to its Decimal Value. Each character in the String should be manually converted to decimal within a loop, and then post the total Decimal Value of the Hexadecimal Value. I got here the codes that I have written but I couldn't identify where I could have gone wrong for an "F4" Hexa (as an example) to have a Decimal Equivalent of "292" instead of "244". I have debugged everything, the code seems fine. Any ideas?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AdvProgCS
{
class Program
{
static int dec=0;
static string hex;
static void Main(string[] args)
{
do{
Console.Write("[Press 0 to Stop] Hexadecimal Value: ");
hex = Console.ReadLine();
if (hex == "0") break;
int length = hex.Length;
for (int i = 0; i < length+1; i++)
{
if (hex[i] == 'A' || hex[i] == 'B' || hex[i] == 'C' || hex[i] == 'D' || hex[i] == 'E' || hex[i] == 'F')
{
if (hex[i] == 'A')
dec+= 10 * Convert.ToInt32(Math.Pow(16, length - 1));
if (hex[i] == 'B')
dec += 11 * Convert.ToInt32(Math.Pow(16, length - 1));
if (hex[i] == 'C')
dec += 12 * Convert.ToInt32(Math.Pow(16, length - 1));
if (hex[i] == 'D')
dec += 13 * Convert.ToInt32(Math.Pow(16, length - 1));
if (hex[i] == 'E')
dec += 14 * Convert.ToInt32(Math.Pow(16, length - 1));
if (hex[i] == 'F')
dec += 15 * (Convert.ToInt32(Math.Pow(16, length - 1)));
}
else
dec += hex[i];
length--;
}
Console.WriteLine("DECIMAL EQUIVALENT: " + dec + "\n");
}
while(hex != "0");
}
}
}
You forgot about the Math.Pow in the dec += hex[i] line where you also have to convert hex[i] from char into a number.
dec += (int)char.GetNumericValue(hex[i]) * (int)Math.Pow(16, length - 1);
Moreover, as observed by Partha:
Also add dec = 0; after your print statement. I think the dec values
are getting adding to itself for each iteration.
Personally, my brain likes it better like this:
static void Main(string[] args)
{
int dec;
string hex;
int decValue;
string hexReversed;
string hexValues = "0123456789ABCDEF";
do
{
Console.Write("[Press 0 to Stop] Hexadecimal Value: ");
hex = Console.ReadLine().ToUpper().Trim();
if (hex != "0")
{
dec = 0;
hexReversed = new String(hex.Reverse().ToArray());
for (int i = 0; i < hexReversed.Length; i++)
{
decValue = hexValues.IndexOf(hexReversed.Substring(i, 1));
if (decValue != -1)
{
dec = dec + (decValue * (int)Math.Pow(16, i));
}
else
{
Console.WriteLine("Invalid Hexadecimal Value!");
break;
}
}
if (dec != 0)
{
Console.WriteLine(String.Format("{0} hex = {1} dec", hex, dec.ToString()));
}
}
} while (hex != "0");
}
Here is my hex to decimal function, much smaller but it doesnt check if the input is a hex value or not.
private int hex2Decimal(string hex)
{
string hexV = "0123456789ABCDEF"; // For the hex values (ex: F=15)
hex = hex.ToUpper().Replace("0X", ""); // Get good string format
int res;
char[] cArr = hex.ToCharArray();
Array.Reverse(cArr); // Reverse hex string
List<int> iArr = new List<int>();
for (int i = hex.Length - 1; i > -1; i--) // Get the bits
iArr.Add(hexV.IndexOf(cArr[i]) * (int)Math.Pow(16, i)); // Calculate each bits and add to an array
res = iArr.ToArray().Sum(); // Calculate all the numbers and add to the final result
return res;
}
This is a complete un compressed IP address 2001:0008:0000:CD30:0000:0000:0000:0101
I need to compress it like this
2001:8:0:CD30::101
But i was only able to compress the zeroes in blocks like this
2001:8:0:CD30:0:0:0:101
using this code
string output = "";
string a = textBox1.Text;
if (a.Length != 39 )
MessageBox.Show("Invalid IP please enter the IPv6 IP in this format 6cd9:a87a:ad46:0005:ad40:0000:5698:8ab8");
else
{
for (int i = 0; i < a.Length; i++)
{
if ((a[i] >= '1' && a[i] <= '9') || (Char.ToLower(a[i]) >= 'a' && Char.ToLower(a[i]) <= 'f') || ((i + 1) % 5 == 0 && a[i] == ':'))
{
output = output + a[i];
}
else if ((a[i]=='0' && a[i-1]==':') || (a[i]=='0' && a[i-1]=='0' && a[i-2]==':') || (a[i]=='0' && a[i-1]=='0' && a[i-2]=='0' && a[i-3]==':'))
{
}
else if (a[i] == '0')
{
output = output + a[i];
}
else
{
MessageBox.Show("Invalid IP please enter the IPv6 IP in this format 6cd9:a87a:ad46:0005:ad40:0000:5698:8ab8");
}
}
textBox2.Text = output;
}
Im using c# but i only need the programming logic about how can whole blocks of zeroes be deleted the problem is there could be more then 1 group of blocks containing all zeros in an ip but only one should be abbreviated.
Was far more tricky than I expected, but here you got the way to do it with regular expressions:
private static string Compress(string ip)
{
var removedExtraZeros = ip.Replace("0000","*");
//2001:0008:*:CD30:*:*:*:0101
var blocks = ip.Split(':');
var regex = new Regex(":0+");
removedExtraZeros = regex.Replace(removedExtraZeros, ":");
//2001:8:*:CD30:*:*:*:101
var regex2 = new Regex(":\\*:\\*(:\\*)+:");
removedExtraZeros = regex2.Replace(removedExtraZeros, "::");
//2001:8:*:CD30::101
return removedExtraZeros.Replace("*", "0");
}
If you would like to achieve the same result without using Regex:
public string Compress(string value)
{
var values = value.Split(",");
var ints = values.Select(i => int.Parse(i, System.Globalization.NumberStyles.HexNumber));
var result = ints.Select(Conversion.Hex);
return string.Join(":", result);
}
Didn't spend time on micro-optimizations (stackalloc, spans, etc.) but this gives an idea. For reference on optimization, you can look at the implementation of the IPAddress.Parse in the .net core. Do mind that result of the IPAddress.Parse will give a different result than the example above:
Compress -> 2001:8:0:CD30:0:0:0:101
IPAddress.Parse -> 2001:8:0:cd30::101
This could be "cleaned" up by moving into some object and other ideas.
Edit:
After chatting with one of my colleagues, I spent some time writing an "optimized" version of this. I haven't spent time cleaning up the code, so maybe one of the future edits will be even cleaner.
public string Compress(string value)
{
Span<char> chars = stackalloc char[value.Length];
const char zero = '0';
const char colon = ':';
int index = 0;
int positionInSegment = 0;
bool startsWithZero;
while (index < _originalValue.Length)
{
startsWithZero = value[index] == zero && positionInSegment == 0;
positionInSegment++;
if (startsWithZero)
{
if (index == value.Length - 1)
{
chars[index] = zero;
break;
}
if (value[index + 1] == colon)
{
chars[index] = zero;
positionInSegment = 0;
index++;
continue;
}
positionInSegment = 0;
index++;
continue;
}
if (value[index] == colon)
{
positionInSegment = 0;
chars[index] = colon;
index++;
continue;
}
chars[index] = value[index];
index++;
}
return chars.ToString();
}
I have also created a public gist for future references:
https://gist.github.com/DeanMilojevic/7b4f1d060ce8cfa191592694b11234d7
I'm trying to decode bitcoin address from Base58 string into byte array, and to do that I rewrited original function from Satoshi repository (https://github.com/bitcoin/bitcoin/blob/master/src/base58.cpp), written in c++, to c# (which I'm using).
Original code
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
bool DecodeBase58(const char *psz, std::vector<unsigned char>& vch) {
// Skip leading spaces.
while (*psz && isspace(*psz))
psz++;
// Skip and count leading '1's.
int zeroes = 0;
while (*psz == '1') {
zeroes++;
psz++;
}
// Allocate enough space in big-endian base256 representation.
std::vector<unsigned char> b256(strlen(psz) * 733 / 1000 + 1); // log(58) / log(256), rounded up.
// Process the characters.
while (*psz && !isspace(*psz)) {
// Decode base58 character
const char *ch = strchr(pszBase58, *psz);
if (ch == NULL)
return false;
// Apply "b256 = b256 * 58 + ch".
int carry = ch - pszBase58;
for (std::vector<unsigned char>::reverse_iterator it = b256.rbegin(); it != b256.rend(); it++) {
carry += 58 * (*it);
*it = carry % 256;
carry /= 256;
}
assert(carry == 0);
psz++;
}
// Skip trailing spaces.
while (isspace(*psz))
psz++;
if (*psz != 0)
return false;
// Skip leading zeroes in b256.
std::vector<unsigned char>::iterator it = b256.begin();
while (it != b256.end() && *it == 0)
it++;
// Copy result into output vector.
vch.reserve(zeroes + (b256.end() - it));
vch.assign(zeroes, 0x00);
while (it != b256.end())
vch.push_back(*(it++));
return true;
}
Mine rewrited c# version
private static string Base58characters = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
public static bool Decode(string source, ref byte[] destination)
{
int i = 0;
while (i < source.Length)
{
if (source[i] == 0 || !Char.IsWhiteSpace(source[i]))
{
break;
}
i++;
}
int zeros = 0;
while (source[i] == '1')
{
zeros++;
i++;
}
byte[] b256 = new byte[(source.Length - i) * 733 / 1000 + 1];
while (i < source.Length && !Char.IsWhiteSpace(source[i]))
{
int ch = Base58characters.IndexOf(source[i]);
if (ch == -1) //null
{
return false;
}
int carry = Base58characters.IndexOf(source[i]);
for (int k = b256.Length - 1; k > 0; k--)
{
carry += 58 * b256[k];
b256[k] = (byte)(carry % 256);
carry /= 256;
}
i++;
}
while (i < source.Length && Char.IsWhiteSpace(source[i]))
{
i++;
}
if (i != source.Length)
{
return false;
}
int j = 0;
while (j < b256.Length && b256[j] == 0)
{
j++;
}
destination = new byte[zeros + (b256.Length - j)];
for (int kk = 0; kk < destination.Length; kk++)
{
if (kk < zeros)
{
destination[kk] = 0x00;
}
else
{
destination[kk] = b256[j++];
}
}
return true;
}
Function that I'm using for converting from byte-array to HexString
public static string ByteArrayToHexString(byte[] source)
{
return BitConverter.ToString(source).Replace("-", "");
}
To test if everything is working correctly I've used test cases found online here (https://github.com/ThePiachu/Bitcoin-Unit-Tests/blob/master/Address/Address%20Generation%20Test%201.txt). Good thing is that 97% of this test are passed correctly but for 3 there is a little error and I do not know where it is coming from. So my ask to you is to point me what could for these test go wrong or where in rewriting I've made an error. Thank you in advance.
The test cases where errors occures are 1, 21 and 25.
1.
Input:
16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM
Output:
000966776006953D5567439E5E39F86A0D273BEED61967F6
Should be:
00010966776006953D5567439E5E39F86A0D273BEED61967F6
21.
Input:
1v3VUYGogXD7S1E8kipahj7QXgC568dz1
Output:
0008201462985DF5255E4A6C9D493C932FAC98EF791E2F22
Should be:
000A08201462985DF5255E4A6C9D493C932FAC98EF791E2F22
25.
Input:
1axVFjCkMWDFCHjQHf99AsszXTuzxLxxg
Output:
006C0B8995C7464E89F6760900EA6978DF18157388421561
Should be:
00066C0B8995C7464E89F6760900EA6978DF18157388421561
In your for-loop:
for (int k = b256.Length - 1; k > 0; k--)
The loop condition should be k >= 0 so that you don't skip the first byte in b256.