First of all, you can't get a high efficiency result.
Compression algorithms almost always have some form of space overhead, which means that they are only effective when compressing data which is sufficiently large that the overhead is smaller than the amount of saved space.
Compressing a string which is only 20 characters long is not too easy, and it is not always possible. If you have repetition, Huffman Coding or simple run-length encoding might be able to compress, but probably not by very much.
I did it without using compression algorithm actually, implementation is not good but maybe it gives you an idea.
The character range you want is between (as decimal)32 and 126, it means you can map your characters to 7 bits instead of 8 bits. You can get rid of 1 byte for each 8 chars.
Disadvantage of this approach is you can't get printable ASCII chars always.
public string CompressString(string str)
{
var sb = new StringBuilder();
foreach (var chr in str)
{
sb.Append(Convert.ToString(chr, 2).PadLeft(7,'0'));
}
int width = (int)(Math.Ceiling((double)sb.Length / 8)) * 8;
string bits = sb.ToString().PadLeft(width, '0');
var list = Enumerable
.Range(0, bits.Length / 8)
.Select(i => bits.Substring(i * 8, 8))
.ToList();
var ascii = new string(list.Select(p => (char)Convert.ToByte(p, 2)).ToArray());
return ascii;
}
public string DecompressString(string str)
{
var sb = new StringBuilder();
foreach (var chr in str)
{
sb.Append(Convert.ToString(chr, 2).PadLeft(8,'0'));
}
string bits = sb.ToString();
bits = bits.Remove(0, bits.Length % 7);
var list = Enumerable
.Range(0, bits.Length / 7)
.Select(i => bits.Substring(i * 7, 7).PadLeft(8,'0'))
.ToList();
var ascii = new string(list.Select(p => (char)Convert.ToByte(p, 2)).ToArray());
return ascii;
}