essukki Ответов: 5

Как сортировать числа между двумя маркерами в текстовом файле


ВИРТУАЛЬНЫЙ КАНАЛ#
У меня есть текстовый файл, содержащий пару чисел, пролитых запятой и сгруппированных специальными маркерами
Как 1000, 2000 и 3000 серийных номеров.
Как я могу отсортировать эти числа в соответствии с первым столбцом?

Вот исходный текстовый файл с именем 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

Сортированный файл должен выглядеть следующим образом:
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


Что я уже пробовал:

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());
               }
         }
    }
}

5 Ответов

Рейтинг:
8

George Swan

Прочитайте все строки из файла. Затем разделите строки на запятые. В строке Foreach выполните следующие действия


  1. Исправьте неправильное форматирование отрицательных чисел, уберите пробел между знаком минус и числом
  2. Постройте кортеж значений (int Group, int FirstItem, int SecondItem) с помощью int.Parse.
  3. Добавьте Кортеж в список кортежей значений.

Наконец, используйте Linq для сортировки списка кортежей по группе, а затем по FirstItem и вывода списка. Что-то вроде этого:



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();

   }


essukki

Спасибо тебе, Джордж, отличный стиль кодирования.

PIEBALDconsult

Сравните свой результат с желаемым.

Рейтинг:
40

PIEBALDconsult

Очень часто, имея дело с текстом, вы обнаружите, что регулярные выражения сделают для вас большую часть тяжелой работы.

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() ) ;    
    }


Maciej Los

Интересное решение с регулярным выражением в использовании. +5!

Рейтинг:
32

OriginalGriff

Храните числа в кортеже или структуре / классе вместо двух массивов: это облегчает si=orting всю нагрузку.
Использование класса очень просто:

public class TwoInts
    {
    public int One { get; set; }
    public int Two { get; set; }
    }

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();
Я бы, наверное, использовал список вместо массива.


Maciej Los

5ed!

essukki

Спасибо OriginalGriff хорошая работа.

PIEBALDconsult

Вы теряете первоначальный интервал?

Рейтинг:
21

CPallini

Попробуйте (измените его в соответствии с вашими точными требованиями):

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));
			}	
			
		}


Maciej Los

5ed!

CPallini

Спасибо!

essukki

Спасибо Вам, CPallini, это очень полезно для моих нужд.

PIEBALDconsult

Однако теряется исходное форматирование.

CPallini

Да, я знаю это ("модифицируйте его в соответствии с вашими точными требованиями" есть причина).

Рейтинг:
19

Maciej Los

Я бы создал класс (как упоминал OriginalGriff), но по-другому:

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>>(); 
}


Использование:
//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);


Для получения более подробной информации, пожалуйста, смотрите:
Файл.Метод ReadAllLines (System.IO) | Microsoft Docs[^]
Файл.Метод WriteAllLines (System.IO) | Microsoft Docs[^]


CPallini

Ваше элегантное решение показывает, насколько ржавым является мой C#.
У меня 5.

Maciej Los

Спасибо, Карло.
Не соглашаться. У нас другой стиль письма, и ваш c# очень хорош.

essukki

Спасибо Мацей Лос это очень полезно

Maciej Los

Всегда пожалуйста.

PIEBALDconsult

Но вы теряете исходное форматирование, я думаю.

Maciej Los

Вы имеете в виду эту строчку: lines[i] = string.Join(",", d);, который - вероятно - добавляет дополнительные скобки?
lines[i] = string.Join(",", {d.Item1, d.Item2}); должен делать свою работу.
Если под оригинальным форматированием вы подразумеваете дополнительное пространство между [-] (знак минус) и номером, то вы правы. Поскольку мой ответ был принят, я думаю, что это не важно для ОП.