2

I'm trying to do a simple string generation based on pattern.
My idea was to use Regex to do simple replace. I've started with simple method:

private static string parseTemplate(string template)
{
    return Regex.Replace(template, @"(\[d)((:)?([\d]+)?)\]", RandomDigit());
}

private static string RandomDigit()
{
    Random r = new Random();
    return r.Next(0, 9).ToString();
}

What this does for now is replacing groups like [d], [d:3] with what supposed to be random digit.
Unfortunately every group is replaced with the same digit, for example if I put test [d][d][d:3] my method will return test 222.
I would like to get different digit in every place, like test 361.

Second problem I have is way to handle length:

right now I must specify [d] for every digit I want, but it would be easier to specify [d:3] and get the same output.

I know that there is a project called Fare, but I would like to do this without this library

For now I only search for [d], but is this method will work fine there won't be a problem to add other groups for example: [s] for special characters or any other type of patters.

Edit1

As it was suggested I changed Random to a static variable like so:

private static string parseTemplate(string template)
    {
        return Regex.Replace(template, @"(\[d)((:)?([\d]+)?)\]", RandomDigit());
    }

    private static Random r = new Random();

    private static string RandomDigit()
    {
        return r.Next(0, 9).ToString();
    }

Problem is that when I call my code like so:

Console.WriteLine(parseTemplate("test [d:2][d:][d]"));
Console.WriteLine(parseTemplate("test [d:2][d:][d]")); 

I get output like this

test 222
test 555

I would like output like this (for example):

test 265
test 962

I think that problem is with Regex.Replace which calls my RandomDigit only once.

4
  • This is an issue with your use of the Random class, you need to initialize it with a different seed or wait for some time between calls to make sure it returns a different value. Commented Jan 18, 2013 at 10:05
  • Check this answer. Commented Jan 18, 2013 at 10:06
  • 1
    Make your Random static: stackoverflow.com/questions/4855756/… Commented Jan 18, 2013 at 10:07
  • 1
    static Random is not an issue here :) I've added some code to my question. Regex.Replace seems to call my random method only once. Commented Jan 18, 2013 at 10:14

3 Answers 3

3

For your first issue: When you call new Random() you are seeding with the same value every time you call the function - initialise a static Random member variable once then return r.Next(0,9).ToString();

Edit:

In answer to your comment, try using MatchEvaluator with a delegate, something like the following (untested):

static string RandomReplacer(Match m)
{
    var result = new StringBuilder();
    foreach (char c in m.ToString())
        result.Append(c == 'd' ? RandomDigit() : c);
    return result.ToString()
}

private static string parseTemplate(string template)
{
    return Regex.Replace(template, @"(\[d)((:)?([\d]+)?)\]", new MatchEvaluator(RandomReplacer));
}

You can then extend this approach to match [d:3] and parse it in your MatchEvaluator accordingly, solving your second issue.

Sign up to request clarification or add additional context in comments.

3 Comments

static Random isn't my problem. It looks like Regex.Replace is calling my random function only once.
Updated answer (the msdn article on the Regex.Replace(String, String, MatchEvaluator) overload has a good example of using Random())
thanks for that :) Got it even simpler with lambda: return Regex.Replace(template, @"(\[d)((:)?([\d]+)?)\]", m => RandomDigit());
1

Assumnig [d:3] means "three random digits", the following MatchEvaluator uses the length (read from group 4) to generate a random digit string:

static string ReplaceSingleMatch(Match m)
{
    int length;
    if (!int.TryParse(m.Groups[1].Value, out length))
        length = 1;
    char[] chars = new char[length];
    for (int i = 0; i < chars.Length; i++)
        chars[i] = RandomDigit()[0];
    return new string(chars);
}

You can then call this as follows:

private static string parseTemplate(string template)
{
    return Regex.Replace(template, @"\[d(?::(\d+))?\]", ReplaceSingleMatch);
}

You might want to then change RandomDigit to return a single char rather than a string, or to take an int and return multiple characters.

10 Comments

You may need to wrap the delegate call in a new MatchEvaluator()
@AlexG I didn't need to, but maybe you do in an earlier version of .NET.
@Rawling - Thanks for that :) awesome work. One more question - how should I change that regex so it will be valid only for [d] and [d:3] (any number after colon) but NOT for [d:] - after colon there is no digit.
@Misiu I'm not sure how to modify your pattern to do this, but if you use @"\[d(?::(\d+))?\]" and read the length from group 1 not group 4, that should work.
@Rawling again thanks for help, maybe for You it's not something special, but for me it's great piece of code :)
|
1
private static string GenerateMask(string mask)
{
    StringBuilder output = new StringBuilder();
    for (int i = 0; i < mask.Length; i++)
    {
        if (mask[i] == 'd' && mask[i - 1] != '\\')
        {
            int quantifier = 1;

            if (mask[i + 1] == ':')
                Int32.TryParse(mask[i + 2].ToString(), out quantifier);

            output.Append(GetRandomDigit(quantifier));
            i += 2;
        }
        else
        {
            if(mask[i] != '\\')
            output.Append(mask[i]);
        }
    }

    return output.ToString();
}

private static string GetRandomDigit(int length)
{
    Random random = new Random();
    StringBuilder output = new StringBuilder();
    while (output.Length != length)
        output.Append(random.Next(0, 9));
    return output.ToString();
}

There's a custom algorithm I just put together for fun mostly and here's the implementation:

Console.WriteLine(GenerateMask(@"Hey Da\d, meet my new girlfrien\d she's d:2"));
//Output: Hey Dad, meet my new girlfriend she's 44

4 Comments

I'll check this out :) Seems that there is an option to do this without regex. Could You show some examples? What do You pass as mask? What do You think would be better?
I updated the original post. Although fun, if the other guys' methods are working for you then I would use theirs myself.
their seems to work better, but Your is easier to extend - add other pattern like c for random character or s for special and so on :)
Watch out for the ArgumentOutOfRangeError when your string starts with a lowercase 'd'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.