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