Click here to Skip to main content
15,897,273 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
VC#
I have a text file contains a pair of numbers spilted by comma and grouped by special markers
Like 1000, 2000 and 3000 serial no.
How can I sort these number according to the first column.

Here the Orginal text file named data.txt:
1000
10, 614
33, 544
- 30, 245
- 25, 311
- 10, 407
0 ,115
20, 436
2000
25, 236
0, 987
- 20, 409
- 60, 201
- 55, 211
- 10 ,415
45, 844
55,678
3000
- 10, 567
0 , 777
10, 955
24, 569
- 35, 300
35, 765
- 45, 335

The sorted file should look like this:
1000
- 30, 245
- 25, 311
- 10, 407
0 ,115
10, 614
20, 436
33, 544
2000
- 60, 201
- 55, 211
- 20, 409
- 10 ,415
0, 987
25, 236
45, 844
55,678
3000
- 45, 335
- 35, 300
- 10, 567
0 , 777
10, 955
24, 569
35, 765


What I have tried:

C#
StreamReader sr= new StreamReader(data.txt);
StreamWriter sw= new StreamWriter(soted.txt,true);

for(str=1000; str <= 3000; str += 1000)
{
  while((line = sr.ReadLine()) != null)
   {
        if line.Contains(str)
         {
              char[] sep = new char[]{','};
              string[] sArr = line.Split(sep);
              for(i=0; i < sArr.Length-1;i++)
               {
                    xArr[i] = Int32.Parse(sArr[0]);
                     yArr[i] = Int32.Parse(sArr[1]);
                      sw.WriteLine("  {0} {1},Convert.ToInt32(xArr[i].ToString(),Convert.ToInt32(yArr[i].ToString());
               }
         }
    }
}
Posted
Updated 2-Sep-20 13:13pm
v2

Very often, when dealing with text, you will find that Regular Expressions will do a lot of the heavy lifting for you.

C#
public static string
    SortNumberList
    (
      string Input
    )
    {
      System.Text.StringBuilder result = new System.Text.StringBuilder ( Input.Length ) ;

      foreach 
      ( 
        System.Text.RegularExpressions.Match m 
      in
        System.Text.RegularExpressions.Regex.Matches 
        ( 
          Input 
        , 
          @"(^|\G)(?'Segment'(?'Marker'(\r\n)?\s*\d+\s*)(?'Pair'\r\n\s*(?'Neg'-?)\s*(?'Key'\d+)\s*,\s*(?'Value'\d+)\s*(?=(\r\n)))*(?=(\r\n)))" 
        )
      )
      {
        result.Append ( m.Groups [ "Marker" ].Value ) ;

        System.Collections.Generic.List<System.Tuple<int,System.Text.RegularExpressions.Capture>> lis =
          new System.Collections.Generic.List<System.Tuple<int,System.Text.RegularExpressions.Capture>> 
          ( m.Groups [ "Pair" ].Captures.Count ) ;

        for ( int i = 0 ; i < m.Groups [ "Pair" ].Captures.Count ; i++ )
        {
          int k = System.Int32.Parse ( m.Groups [ "Key" ].Captures [ i ].Value ) ;

          if ( m.Groups [ "Neg" ].Captures [ i ].Value.Length > 0 )
          {
            k *= -1 ;
          }

          lis.Add ( new System.Tuple<int,System.Text.RegularExpressions.Capture> ( k , m.Groups [ "Pair" ].Captures [ i ] ) ) ;
        }

        lis.Sort() ;

        for ( int i = 0 ; i < lis.Count ; i++ )
        {
          result.Append ( lis [ i ].Item2 ) ;
        }
      }

      return ( result.ToString() ) ;    
    }
 
Share this answer
 
Comments
Maciej Los 3-Sep-20 4:55am    
Interesting solution with Regex in use. +5!
Store the numbers in a tuple or a struct / class instead of two arrays: it makes si=orting a whole load easier.
Using a class is simple:
C#
public class TwoInts
    {
    public int One { get; set; }
    public int Two { get; set; }
    }

C#
TwoInts[] arr = new TwoInts[100];
// Read your file, get each line in the section into two integers x and y
...
arr[i] = new TwoInts() { One = x, Two = y };
...
// Then sort them
arr = arr.OrderBy(v => v.One).ToArray();
I'd probably use a List instead of an array.
 
Share this answer
 
Comments
Maciej Los 2-Sep-20 7:55am    
5ed!
essukki 2-Sep-20 18:01pm    
Thank you OriginalGriff good job.
PIEBALDconsult 2-Sep-20 18:58pm    
Are you losing the original spacing?
Try (modify it according to your exact requirements):
C#
public static void Main(string[] args)
		{
			var info = new  SortedList<int , SortedList<int, int>>();

			using (var sr = new System.IO.StreamReader("data.txt") )
			{
				
				int serial_no=0;
				
				while ( sr.Peek() >= 0)
				{
					string line = sr.ReadLine();
					string [] items = line.Split(new char [] {','});
					if (items.Length == 1)
					{
						serial_no = int.Parse(items[0]);
						info.Add(serial_no, new SortedList<int, int>());
					}
					else if (items.Length == 2)
					{
						info[serial_no].Add(int.Parse(items[0].Replace(" ", string.Empty)), int.Parse(items[1].Replace(" ", string.Empty)));
					}
					else
						throw new Exception(string.Format("invalid line {0}", line));
				}
			}
			foreach (var element in info) 
			{
				Console.WriteLine(element.Key);
				foreach (var p in element.Value)
					Console.WriteLine(string.Format("{0}, {1}", p.Key, p.Value));
			}	
			
		}
 
Share this answer
 
Comments
Maciej Los 2-Sep-20 7:34am    
5ed!
CPallini 2-Sep-20 7:55am    
Thank you!
essukki 2-Sep-20 18:00pm    
Thank you CPallini this is very helpful for my needs.
PIEBALDconsult 2-Sep-20 19:48pm    
Losing the original formatting though.
CPallini 3-Sep-20 2:11am    
Yes, I know that ('modify according it to your exact requirements' is there for a reason).
I'd create a class (as OriginalGriff mentioned) but different way:
C#
public class MyData
{
	
	public MyData(string _header)
	{
		header = Int32.Parse(_header);
	}

	public int header = 0;
	public List<Tuple<int, int>> data = new List<Tuple<int, int>>(); 
}


Usage:
C#
//read original file
string[] lines  = File.ReadAllLines("originalfilename.txt");

List<MyData> mdlist = new List<MyData>();
int i = 0;
MyData md = null;
while(i<lines.Count())
{
    string line = lines[i];
    string[] parts = line.Split(new string[]{", ", " ,", ","}, StringSplitOptions.RemoveEmptyEntries);
    if(parts.Length==0) break;
    if(parts.Length==1)
    {
        //parts[0].Dump();
        md = new MyData(parts[0]);
        mdlist.Add(md);
    }
    else
    {
        parts[0] = parts[0].Replace(" ", null);
        parts[1] = parts[1].Replace(" ", null);
        md.data.Add(new Tuple<int, int>(Int32.Parse(parts[0]), Int32.Parse(parts[1])));
    }
    i++;
}

i =0;
foreach(MyData m in mdlist)
{
    i++;
    lines[i] = m.header.ToString();
    foreach(var d in m.data.OrderBy(x=>x.Item1))
    {
        lines[i] = string.Join(",", d);
        i++;
    }
}
//save new file
File.WriteAllLines("newfilename.txt", lines);


For further details, please see:
File.ReadAllLines Method (System.IO) | Microsoft Docs[^]
File.WriteAllLines Method (System.IO) | Microsoft Docs[^]
 
Share this answer
 
v2
Comments
CPallini 2-Sep-20 7:58am    
Your elegant solution shows how much rusty my C# is.
Have my 5.
Maciej Los 2-Sep-20 8:03am    
Thank you, Carlo.
Disagree. We have different style of writing and your c# is very good.
essukki 2-Sep-20 17:58pm    
Thank you Maciej Los this is very helpful
Maciej Los 2-Sep-20 18:10pm    
You're very welcome.
PIEBALDconsult 2-Sep-20 19:00pm    
But you lose the original formatting I think.

Read All Lines from the file. Then Split the lines on the commas. Foreach line do the following


  1. Fix the incorrect formatting of negative numbers, remove the space between the minus sign and the number
  2. Build a value tuple (int Group, int FirstItem, int SecondItem) using int.Parse.
  3. Add the tuple to a List of value tuples.

Finally, use Linq to Sort the tuple list on Group then by FirstItem and output the list. Something like this:



C#
public static void Main(string[] args)
 {
   List<(int Group, int First, int Second)> tuples = new List<(int Group, int First, int Second)>();
   var lines = File.ReadAllLines(@"C:\Temp\Orig.txt");
   int groupId = -1;
   foreach (var l in lines)
    {
     //Need to fix the faulty formatting of negative numbers, leading and trailing spaces are acceptable
     // It's the space between the minus sign and the number that blows the fuse.
      var line = l.Replace("- ", "-");
      string[] items = line.Split(',');
      if (items.Length > 2) throw new FormatException($"Invalid data: {line}");
       // need to check for Null or Empty as items.Length is never 0
       //because the default is to set [0] to the original string
      if (items.Length == 1 && !string.IsNullOrEmpty(items[0]))//its a group marker,empty lines are ignored
         {
           groupId = int.Parse(items[0]);
         }
      if (items.Length == 2)
         {
           tuples.Add((groupId, int.Parse(items[0]), int.Parse(items[1])));
         }

     }
      var sorted = tuples.OrderBy(t => t.Group).ThenBy(t => t.First);
      int group = -1;
      foreach (var (Group, First, Second) in sorted)
       {
         if (Group != group)
          {

            group = Group;
            Console.WriteLine(group);
           }
           Console.WriteLine($"{First}, {Second}");

       }
          Console.ReadLine();

   }
 
Share this answer
 
v2
Comments
essukki 2-Sep-20 18:04pm    
Thank you George, great coding style.
PIEBALDconsult 2-Sep-20 19:01pm    
Compare your result with the desired result.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900