Облегченный каркасный файл разбора C#

У меня есть файл с форматом, подобным

{1:[...]}{2:[X:11][Y:78][]...}{3:[...]}{4:[...]}{5:
[]
[]
...
[]}$
{1:[...]}{2:[X:43][Y:13][]...}{3:[...]}{4:[...]}{5:
[]
[]
...
[]}$
...

Многоточие указывает на множество повторяющихся структур или повторяющихся строк.

Таким образом, файл состоит из сегментов одинакового формата, разделенных вертикальной чертой.

Каков оптимальный способ извлечь только значения X для каждого сегмента? поэтому мы избегаем загрузки всего файла в память. Оптимален в пространстве и времени. Вероятно, это означает, что нужно избегать загрузки всего файла в память. Возможно, мы могли бы прочитать каждую строку и регулярное выражение, чтобы найти соответствие {2:[X:nn][ и извлечь nn, но это небольшая часть строк.

Но, возможно, есть лучший способ?

Я бы читал файл с помощью StreamReader по одной строке, чтобы не помещать весь файл в память. Похоже, вам просто нужно разобрать строки, начинающиеся с фигурной скобки. Затем извлеките значения X из строки, используя Regex.

jdweng 03.03.2019 22:54

Вы можете прочитать каждый символ, сохранив элемент иерархии, в котором вы сейчас находитесь, и извлечение только нужных значений. Преимущество этого заключается в том, что не имеет значения длина каждой строки, и это скажет вам, недействительны ли ваши данные.

Dour High Arch 03.03.2019 23:45

@jdweng File.ReadLines возвращает Enumerable и будет иметь тот же эффект, что и StreamReader

TheGeneral 03.03.2019 23:52

Если это большие файлы и скорость вызывает беспокойство, я бы просто проанализировал это с помощью fixed(char* p = yourLine), он будет выполнять регулярное выражение. однако, если скорость не имеет значения, регулярное выражение будет более кратким и управляемым.

TheGeneral 03.03.2019 23:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
96
1

Ответы 1

Есть много подходов к этому,

Дано

var lines = File.ReadLines(@"D:\Test.txt");

Примечание : File.ReadLines возвращает Enumerbale, поэтому он будет лениво загружать каждую строку


Опция 1 : регулярное выражение с использованием Положительный взгляд назад и шаблона (?<=2:\[X:)\d+

foreach (var line in lines)
{
   var match = Regex.Match(line,@"(?<=2:\[X:)\d+");
   if (match.Success)
      Console.WriteLine(match.Value);  
}

Вариант 2 : Просто string.Split

foreach (var line in lines)
{
   var results = line.Split(new[] { "2:[X:", "][Y:" }, StringSplitOptions.RemoveEmptyEntries);

   if (results.Length>1)
      Console.WriteLine(results[1]);
}

Вариант 3 : «Возможно» более эффективный подход с использованием указателиfixed и unsafe

public static unsafe (bool found, int value) ParseLine(string line)
{
   const string prefix = "2:[X:"; 
   fixed (char* pLine = line,pPrefix = prefix)
   {
 
      var pLen = line.Length + pLine;
      var found = false;
      var result = 0;
      var i = 0;
      for (char* p = pLine ,pP = pPrefix; p < pLen; p++)
      {
         if (!found )
         {
            if ( *p == *(pP+i)) i++;
            if ( i ==prefix.Length) found = true;
            continue;
         }
         
         if (*p < '0' || *p > '9')
            break;

         result = result * 10 + *p - '0';

 
      }

      return (found, result);
   }
}
    
...

var results = File.ReadLines(@"D:\Test.txt")
                  .Select(ParseLine)
                  .Where(result => result.found)
                  .Select(result => result.value);

foreach (var result in results)
   Console.WriteLine(result);

Примечание : речь идет не об избиении регулярных выражений, а о разных подходах.

Я не проверял это, однако я подозреваю, что указатели будет самым быстрым, split будет следующим, а регулярное выражение, возможно, будет самым медленным (даже при использовании скомпилированного), однако это самый читаемый и поддерживаемый, а также надежный подход ( поэтому и ставлю первым)

Ориентиры

+----------+------------+-----------+-----------+
|  Method  |    Mean    |   Error   |  StdDev   |
+----------+------------+-----------+-----------+
| RegEx    | 3,358.3 us | 65.169 us | 66.923 us |
| Split    | 1,980.9 us | 38.440 us | 48.614 us |
| Pointers | 287.4 us   | 4.396 us  | 4.112 us  |
+----------+------------+-----------+-----------+

Тестовый код

public class Test
{
   private Regex _regex;

   private string[] data;

   [GlobalSetup]
   public void Setup()
   {
      _regex = new Regex(@"(?<=2:\[X:)\d+", RegexOptions.Compiled);

      data = File.ReadLines(@"D:\Test3.txt")
                 .ToArray();
   }

   [Benchmark]
   public List<int> RegEx()
   {
      return data.Select(line => _regex.Match(line))
                 .Where(x => x.Success)
                 .Select(match => int.Parse(match.Value))
                 .ToList();
   }

   [Benchmark]
   public List<int> Split()
   {
      return data.Select(line => line.Split(new[] { "2:[X:", "][Y:" }, StringSplitOptions.RemoveEmptyEntries))
                 .Where(results => results.Length > 1)
                 .Select(results => int.Parse(results[1]))
                 .ToList();
   }

   [Benchmark]
   public List<int> Pointers()
   {
      return data.Select(ParseLine)
                 .Where(result => result.found)
                 .Select(result => result.value)
                 .ToList();
   }

   public static unsafe (bool found, int value) ParseLine(string line)
   {
      const string prefix = "2:[X:"; 
      fixed (char* pLine = line,pPrefix = prefix)
      {
    
         var pLen = line.Length + pLine;
         var found = false;
         var result = 0;
         var i = 0;
         for (char* p = pLine ,pP = pPrefix; p < pLen; p++)
         {
            if (!found )
            {
               if ( *p == *(pP+i)) i++;
               if ( i ==prefix.Length) found = true;
               continue;
            }
            
            if (*p < '0' || *p > '9')
               break;

            result = result * 10 + *p - '0';

    
         }

         return (found, result);
      }
   }
}

Гораздо эффективнее использовать if (line.StartsWith("{")) перед использованием Regex.

jdweng 04.03.2019 05:46

@jdweng да, хорошее замечание, то же самое со всеми ними на самом деле

TheGeneral 04.03.2019 05:47
x: может появляться в других типах блоков, кроме 2: , однако меня интересует только X: внутри блока 2:.
BaltoStar 04.03.2019 06:31

Другие вопросы по теме