0

I am currently trying to parse a .txt file containing information listed like this: name/ID number/email/GPA. Here are a few lines showing what the text file looks like.

(LIST (LIST 'Doe 'Jane 'F ) '8888675309 'jfdoe@mail.university.edu 2.3073320999676614 )

(LIST (LIST 'Doe 'John 'F ) 'NONE 'johnfdoe@mail.university.edu 3.1915725161177115 )

(LIST (LIST 'Doe 'Jim 'F ) '8885551234 'jimdoe@mail.university.edu 3.448215586562192 )

In my current code all I am doing is printing the text file line by line to a console window.

static void Main(string[] args)
    {

        StreamReader inFile;
        string inLine;

        if (File.Exists("Students.txt"))
        {
            try
            {
                inFile = new StreamReader("Students.txt");
                while ((inLine = inFile.ReadLine()) != null)
                {
                    Console.WriteLine(inLine);

                }
            }
            catch (System.IO.IOException exc)
            {
                Console.WriteLine("Error");

            }
            Console.ReadLine();
        }
    }

I need to able to, for example, find all the students that have a GPA above 3.0 and print their name and GPA to another text file. I understand how to print to another file, however, I am unsure how to access the individual columns, such as the GPA, since this file does not seem to have any common delimiters that would make using a Split() practical. Any help or insight on how to accomplish this would be appreciated.

Victor Wilson
  • 1,720
  • 1
  • 11
  • 22
  • Welcome to StackoverFlow. Please [take the tour](https://stackoverflow.com/tour), read about [how to ask good questions](https://stackoverflow.com/help/how-to-ask) and learn about [how to create a Minimal, Complete and Verifiable Example](https://stackoverflow.com/help/mcve). – Gaurang Dave Mar 30 '18 at 02:19
  • These are a bit unusual to see in a text file, but they are a well known form. They are called S-expressions and form basis of the Lisp family of programming languages. – Mike Zboray Mar 30 '18 at 02:55
  • Check my answer below, it will give you basic idea what you need to do. – Gaurang Dave Mar 30 '18 at 03:35
  • Possible duplicate of [S-Expressions parsing](https://stackoverflow.com/questions/14675335/s-expressions-parsing) – dumetrulo Mar 30 '18 at 08:40

3 Answers3

2

IMPORTANT

I considered that the provided string in your question has a fixed format as shown.

IMPLEMENTATION

First, you need to create a class that is blueprint of the information you are getting from the string. It will give you a container to hold a meaningful information about the data.

public class StudentInfo
{
    public string Name { get; set; }
    public string Number { get; set; }
    public string Email { get; set; }
    public double GPA { get; set; }
}

Following is an example how to parse the string (string from your question) and convert it to relative information. I assume that you can read/write files in C#. This sample demonstrates parsing and storing iformation in List. You can further use this to write files.

In you code, you are reading lines and that is why in this sample, I tried to read lines from string so you can understand it better.

I created this sample in C# Console application.

static void Main(string[] args)
{
    List<StudentInfo> studentInfo = new List<StudentInfo>();

    string input = "(LIST(LIST 'Abbott 'Ashley 'J ) '8697387888 'ajabbott@mail.university.edu 2.3073320999676614 )" + Environment.NewLine +
    "(LIST(LIST 'Abbott 'Bradley 'M ) 'NONE 'bmabbott@mail.university.edu 3.1915725161177115 )" + Environment.NewLine +
    "(LIST(LIST 'Abbott 'Ryan 'T ) '8698689793 'rtabbott@mail.university.edu 3.448215586562192 )";

    string[] lines = input.Split(new[] { Environment.NewLine }, StringSplitOptions.None);

    if (lines != null && lines.Count() > 0)
    {
        foreach (var line in lines)
        {
            var data = line.Replace("(LIST(LIST ", string.Empty)
                .Replace(")", string.Empty)
                .Replace("'", string.Empty)
                .Trim()
                .Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

            if (data != null && data.Count() > 0)
            {
                studentInfo.Add(
                    new StudentInfo()
                    {
                        Name = data[0] + " " + data[1] + " " + data[2],
                        Number = data[3],
                        Email = data[4],
                        GPA = Convert.ToDouble(data[5])
                    });
            }
        }
    }

    // GET STUDENTS WHO GOT GPA > 3  (LINQ QUERY)
    if (studentInfo.Count > 0)
    {
        var gpaGreaterThan3 = studentInfo.Where(s => s.GPA >= 3).Select(s => s).ToList();

        if (gpaGreaterThan3 != null && gpaGreaterThan3.Count > 0)
        {
            // LOOP gpaGreaterThan3 TO PRINT STUDENT DATA
            foreach (var stud in gpaGreaterThan3)
            {
                Console.WriteLine("Name: " + stud.Name);
                Console.WriteLine("Number: " + stud.Number);
                Console.WriteLine("Email: " + stud.Email);
                Console.WriteLine("GPA: " + stud.GPA);
                Console.WriteLine(string.Empty);
            }
        }
    }

    Console.ReadLine();
}
Gaurang Dave
  • 3,956
  • 2
  • 15
  • 34
  • Thank you, I really appreciate the answer, it does work. However I probably wasn't as clear as I should have been about exactly what I need it to do.Ultimately I need to be able to output everyone who has matching last names and a GPA higher than 3.0. This code does a great job of checking their GPA's but I need to also make last name comparisons. Do you have any further insight on how to do this? – Charles Roberts Mar 30 '18 at 23:25
  • As per your initial question, there is not clear that there is need to parse first name and last name. It's very simple to do so. I think you should try it once. In name field, do spilt using space and get the first and last name. Else i will help you to do so. Please mark this as answer if it was useful. – Gaurang Dave Mar 31 '18 at 06:13
0

Try this:

            var data = inLine.Replace("(LIST(LIST ", string.Empty)
                        .Replace(")", string.Empty)
                        .Replace("'", string.Empty)
                        .Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
Matt.G
  • 3,586
  • 2
  • 10
  • 23
0

There are many ways to go about this, but most importantly you need to consider variations to the string format that might trip up any of the approaches

  1. is gpa field always present and at the end?
  2. will it have a defined identifiable format etc
  3. Can there be more than one and if so which one would you pick etc

Below are a couple of approaches with assumptions. You would have to adjust the code per your assumption and how critical this piece of code would be.

    // split on both space and closing bracket
    // Assumption: GPA field is present and at the end
    Console.WriteLine(line.Split(new[] { " ", ")" }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault());

    // regex for gpa defined as digit followed by literal . followed by one or more digits
    // Assumption: GPA field is present once somewhere in the string.
    // No other token conflicts with similar format
    var gpaRegex = new Regex(@"\d\.\d+");
    Console.WriteLine(gpaRegex.Matches(line)[0]);

See https://dotnetfiddle.net/6Xy0uW for working example

See https://regex101.com/r/P1D7zf/1 for the regex in action where you might try more strict variations

RnP
  • 390
  • 1
  • 8