Как преобразовать многострочную строку с разделителями в список

#c# #split #delimiter

Вопрос:

У меня есть строка, разделенная новой строкой.
Я могу разделить его на 4 разных элемента списка, но мне также нужно разделить содержимое каждой строки, где элементы используют | символ в качестве разделителя.

Во внутренних значениях используются такие теги, как BR= , KS= . Я хочу использовать значение этих тегов для создания новых объектов класса.
Класс имеет имя BIRIM ; у него есть имена свойств, соответствующие тегам в строках.

Моя Струна:

 BR=PALET90|KS=90|IS=1
BR=PALET60|KS=60|IS=1
BR=EUROPALET|KS=55|IS=1
BR=EUROPALET66|KS=66|IS=1
BR=PALET|KS=75|IS=1
 

Мой Текущий Код:

  public class BIRIM {
  public string BR {get;set;}
  public int KS {get;set;}
  public int IS {get;set;}
}

 string birim = node2["BIRIMLER"]?.InnerText;

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

Ответ №1:

Технически вы можете Split несколько раз:

  using System.Linq;

 ...

 string source = 
   @"BR=PALET90|KS=90|IS=1
     BR=PALET60|KS=60|IS=1
     BR=EUROPALET|KS=55|IS=1
     BR=EUROPALET66|KS=66|IS=1
     BR=PALET|KS=75|IS=1";

  ...

  var myList = source
    .Split(new char[] { 'r', 'n' }, StringSplitOptions.RemoveEmptyEntries)
    .Select(line => line
       .Split('|')
       .Select(item => item.Split('='))
       .ToDictionary(item => item[0].Trim(), item => item[1]))
    .Select(dict => new BIRIM() {
      BR = dict["BR"],
      KS = int.Parse(dict["KS"]),
      IS = int.Parse(dict["IS"])
    })
    .ToList();
 

Тем не менее, я предлагаю реализовать TryParse метод внутри BIRIM класса, пусть этот класс разберется сам, когда это необходимо:

 using System.Text.RegularExpressions;

...

public class BIRIM {

  ...

  public static bool TryParse(string source, out BIRIM result) {
    result = null;

    if (string.IsNullOrWhiteSpace(source))
      return false;

    string br = Regex.Match(source, @"BRs*=s*(w )").Groups[1].Value;
    string KS = Regex.Match(source, @"KSs*=s*([0-9] )").Groups[1].Value;
    string IS = Regex.Match(source, @"ISs*=s*([0-9] )").Groups[1].Value;

    if (!string.IsNullOrEmpty(br) amp;amp; 
         int.TryParse(KS, out int aks) amp;amp; 
         int.TryParse(IS, out int ais)) {
      result = new BIRIM() {
        BR = br,
        KS = aks,
        IS = ais,
      };

      return true;
    }

    return false;
  }
}
 

Затем вы можете реализовать загрузку как

   var myList = source
    .Split(new char[] { 'r', 'n' }, StringSplitOptions.RemoveEmptyEntries)
    .Select(line => BIRIM.TryParse(line, out var value) ? value : null)
    .Where(value => value != null)
    .ToList();
 

Комментарии:

1. В методах анализа всегда есть проблема: каким должно быть правильное поведение, если анализ не удался? здесь вы просто игнорируете «неправильные» строки. Это означает, что на самом деле вы проглатываете проблему (исходная строка каким-то образом неверна?) и игнорируете ее. В зависимости от обстоятельств, это может быть не лучший выбор. В общем, я предпочитаю обрабатывать как ошибку то, что является «логической» ошибкой, вместо того, чтобы продолжать как ни в чем не бывало: обычно это приводит к дальнейшим ошибкам / неправильному поведению в программе, которые, вероятно, труднее понять и исправить

2. Джан Паоло: Я думал, что продемонстрирую принцип единой ответственности с TryParse помощью : класс должен анализировать сам для себя . Что касается реализации, мы можем игнорировать ошибки синтаксического анализа или создавать исключения, или даже собирать ошибки, а затем создавать исключения со списком проблем. То же самое для TryParse : мой код допускает обмен фрагментами (сначала KS, затем BR), что не является необходимой лучшей политикой

3. нет ничего плохого в методе пробного анализа сам по себе и в том, как вы реализовали его в своем примере, также удовлетворяющем SRP. Именно его использование «опасно», когда кто-то просто игнорирует ложное возвращаемое значение. Иногда это может быть правильным выбором, большую часть времени я думаю (и, возможно, в этом случае тоже), что программа должна каким-то образом решить проблему «ввод был каким-то образом неправильным, я должен что-то сделать, а не просто игнорировать неправильный ввод». Конечно, решение о том, как обрабатывать неверный ввод, является обязанностью вызывающего, а не самого класса BRIM.

Ответ №2:

попробуйте это вместо Environment.NewLine :

  string[] birimlerim = birim.Split(new string[] { "rn" }, StringSplitOptions.None);
 

Ответ №3:

Вам придется разделиться несколько раз

Одна из реализаций может быть следующей, но, пожалуйста, подумайте о том, чтобы разделить ее на большее количество функций, она будет более читаемой, и вы сможете проанализировать преобразованное значение.

     private static List<BIRIM> ParseBirim(string birimText)
    {
        var birims = new List<BIRIM>();

        string[] birimlerim = birimText.Trim().Split(Environment.NewLine);

        foreach (string birim in birimlerim)
        {
            string[] bki = birim.Split('|');

            birims.Add(new BIRIM
            {
                BR = bki[0].Split('=')[1],
                KS = Convert.ToInt32(bki[1].Split('=')[1]),
                IS = Convert.ToInt32(bki[2].Split('=')[1]),
            });
        }

        return birims;
    }
 

Ответ №4:

Еще один вариант.
Проанализируйте весь многострочный контент с помощью регулярных выражений.Matches() (указание RegexOptions.Multiline ), повторите элементы коллекции MatchCollection для создания новых BIRIM объектов с извлеченными значениями.

 string pattern = @"^BR=(?<br>.*?)|KS=(?<ks>.*?)|IS=(?<is>.*?)$";
string innerText = node2["BIRIMLER"]?.InnerText;

var birims = Regex.Matches(innerText, pattern, RegexOptions.Multiline)
    .OfType<Match>().Select(m => 
        new BIRIM(
            m.Groups["br"].Value,
            int.Parse('0'   m.Groups["ks"].Value.Trim()),
            int.Parse('0'   m.Groups["is"].Value.Trim())))
    .ToList();
 

Это обрабатывает значение null KS и IS значения, преобразуя их в пустые/нулевые значения 0 .
Адаптируйтесь по мере необходимости.


Я добавил в ваш BIRIM класс конструктор, который принимает аргументы, соответствующие типу его свойств.
Если вы не можете изменить структуру классов, используйте синтаксис ( new T() { PropertyName = PropertyValue } ):

 public class BIRIM
{
    public BIRIM(string sbr, int sks, int sis) {
        BR = sbr; KS = sks; IS = sis;
    }

    public string BR { get; set; }
    public int KS { get; set; }
    public int IS { get; set; }
}