0

I've a text file and I've to read the text file and then I've to convert the file data in the form of a table. The file is in this form

{KeyValuePair}
{
    Key1 = Value1 {next}
    Key2 = Value2 {next}
    Key3 = Value3 {next}
    Key4 = {KeyValuePair}   {
                                KeyA = ValueA {next}
                                KeyB = ValueB {next}
                                KeyC = ValueC {next}
                            }
}

and I need a output like this

enter image description here

My logic code is Here

StreamReader reader = new StreamReader("C:\\Users\\Kaushik Kishore\\Documents\\TextToRead.txt");
            string data = reader.ReadToEnd();
            //string[] stringSeparater = new string[] { "{KeyValuePair}" };
            //string[] getData = data.Split(stringSeparater, StringSplitOptions.None);
            //string[] separater = new string[] { "{next}" };
            //string[] nextSplit = data.Split(separater, StringSplitOptions.None);
            string pattern = @"(=)|(next)|(KeyValuePair)|({)|(})";
            string[] output = Regex.Split(data, pattern);
            foreach (string one in output)
            {
                Response.Write(one);

            }

so thing I'm facing the problem is how to write the actual logic to extract the desired string. Next specifies that We have to change the row in the table. each time the next keyword will occur I've to post the data in new Row. Thanks in Advance

EDIT I've done some effort and write some code This is printing the data fine now I want to know how to pass the data from controller to view. when the data is coming in loop parts.

public ActionResult Index()
        {


            StreamReader reader = new StreamReader("C:\\Users\\Kaushik Kishore\\Documents\\Text2.txt");
            string data = reader.ReadToEnd();
            // replacing all tabs white space new line and everything
            string trimmedData = Regex.Replace(data, @"\s", "");
            string pattern = @"({next})|({KeyValuePair}{)|(}{next})";
            string[] output = Regex.Split(trimmedData, pattern);
            int length = output.Length;
            int count = 0;
            foreach (string one in output)
            {
                count++;
                if (one == "{KeyValuePair}{")
                {
                    Response.Write("Table Create</br>");
                }
                else if (count == length)
                {
                    string[] last = one.Split('=');
                    foreach (string lastVal in last)
                    {
                        Response.Write(lastVal.Substring(0,lastVal.Length-1));
                        Response.Write('|');
                    }
                }
                else
                {                                        
                    string[] keyVal = one.Split('=');
                    foreach (string val in keyVal)
                    {
                        if (val == "{next}")
                        {
                            Response.Write("</br>");                            
                        }
                        else if (val == "}{next}")
                        {
                            Response.Write("Subtable End</br>");                            
                        }
                        else if (val == "}")
                        {
                            Response.Write("");                            
                        }
                        else
                        {
                            Response.Write(val);
                            Response.Write("|");
                        }
                    }
                }
            }
            reader.Close();
            return View();
        }
  • @SamLeach I've mentioned the text file format. above please go through that. –  Jan 07 '14 at 14:10

3 Answers3

0

if you use this little pattern, and if you use it recursively on the value capturing group, I think you can obtain what you want:

string pattern = @"(?>\s*(?<key>[^\s=]+)\s*=\s*|^\s*)(?>{KeyValuePair}\s*{\s*(?<value>(?>(?<c>{)|(?<-c>})|[^{}]+)+(?(c)(?!)))\s*}|(?<value>[^\s{]+)\s*(?<next>{next})\s*)";

pattern details:

(?>                            # possible begin of the match
    \s*(?<key>[^\s=]+)\s*=\s*    # a keyname
  |                             # OR
    ^\s*                         # the start of the string
)

(?>
    # case KeyValuePair #
    {KeyValuePair} \s* { \s*  
    (?<value> 
        (?>(?<c>{)|(?<-c>})|[^{}]+)+ (?(c)(?!)) # content inside balanced curly brackets*
    )
    \s* }
  |    OR
    # case next #
    (?<value>
        [^\s{]+  # all that is not a white character or an opening curly bracket
    )
    \s*
    (?<next> {next} )\s* # the goal of this capture is to know in which case you are
)

(*)You can find more explanations about balancing groups here: What are regular expression Balancing Groups?

The idea is to write a recursive method that will call itself when the pattern matches the "keyValuePair" case. In the "next" case, the method records only the key/value in an array (or this kind of structure). The method must return this kind of array.

Community
  • 1
  • 1
Casimir et Hippolyte
  • 88,009
  • 5
  • 94
  • 125
  • Can you please explain how can i make my own pattern ? I'm new to this `C#` so if you can explain me this I'll be grateful. and this is showing error in `\s` sequence. –  Jan 07 '14 at 16:59
  • @Kaushik: You must use a raw string (`string pattern = @"...";`) if you don't want to double each backslash. I am not a C# expert, this is the reason why I can't give you the full code for the method, but the idea is here (it's a recursive function). Since the pattern is a bit long I can't describe each symbol, however you will find more informations in http://regular-expressions.info , you can test the pattern in http://regexstorm.net/tester – Casimir et Hippolyte Jan 07 '14 at 18:24
  • I'm trying this If I'll get the answer I'll mark this Up. Thanks for your support. –  Jan 08 '14 at 04:57
0

I created a parser-based solution that outputs a dictionary containing the key value pairs. It can nest {KeyValuePair}s as deep as you want.

Use like this:

string data = File.ReadAllText("data.txt");
var p = new Parser(text);
Dictionary<string, Value> dictionary = p.Parse();

A value can be either a string or a dictionary:

public abstract class Value { }

public class StringValue : Value
{
    public string Value { get; private set; }
    public StringValue(string value)
    {
        this.Value = value;
    }
}

public class DictionaryValue : Value
{
    public Dictionary<string, Value> Values { get; private set; }
    public DictionaryValue()
    {
        this.Values = new Dictionary<string, Value>();
    }
}

This allows for error reporting:

public class ParseError : Exception
{
    public ParseError(string message)
        : base(message) { }
}

The parser consists of two things. The tokenizer, which transforms the input text to a stream of tokens:

KeyValuePair, OpenBracket, KeyOrValue(Key1) , Assign, KeyOrValue(Value1) , Next, KeyOrValue(Key2) , Assign, KeyOrValue(Value2) , Next, KeyOrValue(Key3) , Assign, KeyOrValue(Value3) , Next, KeyOrValue(Key4) , Assign, KeyValuePair, OpenBracket, KeyOrValue(KeyA) , Assign, KeyOrValue(ValueA) , Next, KeyOrValue(KeyB) , Assign, KeyOrValue(ValueB) , Next, KeyOrValue(KeyC) , Assign, KeyOrValue(ValueC) , Next, CloseBracket, CloseBracket, End

And then the parser, which transforms the token stream into a dictionary.

Here is the complete code:

public class Parser
{
    private Tokenizer tk;
    public Parser(string text)
    {
        this.tk = new Tokenizer(text);
    }
    public Dictionary<string, Value> Parse()
    {
        Stack<Dictionary<string, Value>> dictionaries = new Stack<Dictionary<string, Value>>();

        Token t;

        while ((t = tk.ReadToken()) != Token.End)
        {
            switch (t)
            {
                case Token.KeyValuePair:
                    t = tk.ReadToken();
                    if (t != Token.OpenBracket)
                        throw new ParseError("{KeyValuePair} should be followed by a '{'");
                    dictionaries.Push(new Dictionary<string, Value>());
                    break;
                case Token.CloseBracket:
                    if (dictionaries.Count > 1)
                        dictionaries.Pop();
                    break;
                case Token.KeyOrValue:
                    string key = tk.TokenValue;
                    t = tk.ReadToken();
                    if (t != Token.Assign)
                        throw new ParseError("Key should be followed by a '='");
                    t = tk.ReadToken();
                    if (t == Token.KeyValuePair)
                    {
                        var value = new DictionaryValue();
                        dictionaries.Peek().Add(key, value);
                        dictionaries.Push(value.Values);
                    }
                    else if (t != Token.KeyOrValue)
                        throw new ParseError("Value expected after " + key + " =");
                    else
                    {
                        string value = tk.TokenValue;
                        dictionaries.Peek().Add(key, new StringValue(value));
                        t = tk.ReadToken();
                        if (t != Token.Next)
                            throw new ParseError("{next} expected after Key value pair (" + key + " = " + value + ")");
                    }
                    break;
                case Token.Error:
                    break;
                default:
                    break;
            }
        }
        return dictionaries.Peek();
    }

    private class Tokenizer
    {
        private string _data;
        private int currentIndex = 0;
        private string tokenValue;

        public string TokenValue
        {
            get { return tokenValue; }
        }

        public Tokenizer(string data)
        {
            this._data = data;
        }

        public Token ReadToken()
        {
            tokenValue = string.Empty;
            if (currentIndex >= _data.Length) return Token.End;

            char c = _data[currentIndex];
            if (char.IsWhiteSpace(c))
            {
                currentIndex++;
                return ReadToken();
            }
            else if (c == '{')
            {
                if (TryReadBracketedToken("KeyValuePair"))
                {
                    currentIndex++;
                    return Token.KeyValuePair;
                }
                else if (TryReadBracketedToken("next"))
                {
                    currentIndex++;
                    return Token.Next;
                }
                else
                {
                    currentIndex++;
                    return Token.OpenBracket;
                }
            }
            else if (c == '}')
            {
                currentIndex++;
                return Token.CloseBracket;
            }
            else if (c == '=')
            {
                currentIndex++;
                return Token.Assign;
            }
            else
            {
                StringBuilder valueBuilder = new StringBuilder();
                while (currentIndex < _data.Length && !char.IsWhiteSpace(c))
                {
                    valueBuilder.Append(c);
                    currentIndex++;
                    c = _data[currentIndex];
                }
                tokenValue = valueBuilder.ToString();
                return Token.KeyOrValue;
            }
        }

        private bool TryReadBracketedToken(string token)
        {
            bool result = _data.Length > currentIndex + token.Length + 2
                        && _data.Substring(currentIndex + 1, token.Length + 1) == token + "}";
            if (result)
            {
                currentIndex++;
                currentIndex += token.Length;
            }
            return result;
        }
    }

    private enum Token
    {
        KeyValuePair,
        Next,
        OpenBracket,
        CloseBracket,
        Assign,
        KeyOrValue,
        End,
        Error
    }
}

public abstract class Value { }

public class StringValue : Value
{
    public string Value { get; private set; }
    public StringValue(string value)
    {
        this.Value = value;
    }
}

public class DictionaryValue : Value
{
    public Dictionary<string, Value> Values { get; private set; }
    public DictionaryValue()
    {
        this.Values = new Dictionary<string, Value>();
    }
}

public class ParseError : Exception
{
    public ParseError(string message)
        : base(message) { }
}
Bas
  • 26,772
  • 8
  • 53
  • 86
  • First of all thank you so much for the effort. I'm beginner to the c# so i'm not able to understand the code fully. if you'll wish i can show you my code what I've done till now.That is working but I'm not able to send the data from controller to view due to some looping statement –  Jan 08 '14 at 10:12
0

This will be your controller part

public ActionResult Index()
    {
        ViewBag.DisplayTable = GetKeyValueDisplayContent(@"YourFilePath.Txt");
        return View();
    }

    private string GetKeyValueDisplayContent(string fileToRead)
    {
        // 01 Get Data
        string DataToProcess = GetDataToProcess(fileToRead);

        // 02 Cleaning Data (replacing all tabs white space new line and everything)
        DataToProcess = CleanDataToProcess(DataToProcess);

        // 03 Retrieve Array from Data format
        string[] output = GetDataInArray(DataToProcess);

        // 04 Displaying Result
        string DrawTable = GetDisplayHTML(output);
        return DrawTable;

    }

    private string GetDataToProcess(string fileToRead)
    {

        StreamReader reader = new StreamReader(fileToRead);
        string data = reader.ReadToEnd();
        reader.Close();

        return data;
    }

    private string CleanDataToProcess(string dataToProcess)
    {
        return Regex.Replace(dataToProcess, @"\s", "");
    }

    private string[] GetDataInArray(string dataToProcess)
    {
        string pattern = @"({next})|({KeyValuePair}{)|(}{next})";
        string[] output = Regex.Split(dataToProcess, pattern);

        return output;
    }

    private string GetDisplayHTML(string[] output)
    {
        int length = output.Length;
        int count = 0;
        StringBuilder OutputToPrint = new StringBuilder();


        foreach (string one in output)
        {

            if (one == "{KeyValuePair}{")
            {
                count++;
                if (count >= 2)
                {
                    OutputToPrint.Append("<td><table border = \"1\">");
                }
                else
                {
                    OutputToPrint.Append("<table border = \"1\">");
                }
            }
            else if (one.Contains("=") == true)
            {
                string[] keyVal = Regex.Split(one, @"=");
                OutputToPrint.Append("<tr>");
                foreach (string val in keyVal)
                {
                    if (val != "")
                    {
                        OutputToPrint.Append("<td>");
                        OutputToPrint.Append(WebUtility.HtmlEncode(val));
                        OutputToPrint.Append("</td>");

                    }
                }

            }
            else if (one.Equals("{next}"))
            {
                OutputToPrint.Append("</tr>");
            }
            else if (one.Contains("}{next}") == true)
            {
                OutputToPrint.Append("</table></td>");
            }
            else if (one == "}")
            {
                OutputToPrint.Append("</table>");
            }
            else { }
        }

        return OutputToPrint.ToString();
    }

This will be the View

<div>
@Html.Raw(ViewBag.DisplayTable)
</div>

Hope You'll find this well

Kaushik
  • 2,072
  • 1
  • 23
  • 31