Подтвердить что ты не робот

С#: Разделить строку и присвоить результат нескольким строковым переменным

У меня есть строка с несколькими полями, разделенными определенным символом, примерно так:

А, В, С

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

my ($varA, $varB, $varC) = split (/,/, $string);

Каков самый простой и элегантный способ добиться того же результата в С#?

Я знаю, что я могу разбить на массив:

string[] results = string.Split(',');

Но тогда мне нужно будет получить доступ к полям через их индекс, например. Результаты [2]. Это трудно читать и подвергать ошибкам - считайте, что у вас нет 3 buth 30 полей. По этой причине я предпочитаю иметь каждое значение поля в своей собственной именованной переменной.

4b9b3361

Ответ 1

Я согласен. Скрытие раскола в классе адаптера кажется хорошим подходом и довольно хорошо сообщает ваше намерение:

public class MySplitter
{
     public MySplitter(string split)
     {
         var results = string.Split(',');
         NamedPartA = results[0];
         NamedpartB = results[1];
     }

     public string NamedPartA { get; private set; }
     public string NamedPartB { get; private set; }
}

Ответ 2

Вы можете использовать Tuples (добавленный в .Net 4). Кортежи в MSDN

Это:

public class MySplitter
{
     public MySplitter(string split)
     {
         var results = split.Split(',');
         NamedPartA = results[0];
         NamedpartB = results[1];
     }

     public string NamedPartA { get; private set; }
     public string NamedPartB { get; private set; }
}

Может быть достигнуто с чем-то вроде этого:

public Tuple<string,string> SplitIntoVars(string toSplit)
{
   string[] split = toSplit.Split(',');
   return Tuple.Create(split[0],split[1]);
}

С помощью Tuple вы можете использовать:

var x = SplitIntoVars(arr);
// you can access the items like this:    x.Item1 or x.Item2 (and x.Item3 etc.)

Вы также можете создать Tuple для использования Tuple<string,int> и т.д.

Также... Мне не очень нравятся параметры, поэтому вы эмулируете возвращение нескольких значений, используя (и, очевидно, также разных типов). это:

public void SplitIntoVariables(string input, out a, out b, out c)
{
    string pieces[] = input.Split(',');
    a = pieces[0];
    b = pieces[1];
    c = pieces[2];
}

превращается в это:

public Tuple<string,string,string> SplitIntoVariables(string[] input)
    {
        string pieces[] = input.Split(',');
        return Tuple.Create(pieces[0],pieces[1],pieces[2]);
    }

Другими (более изобретательными) параметрами могут быть создание ExpandoObject (dynamic), который содержит ваши значения (что-то похожее на ViewBag в ASP.NET MVC)

Ответ 3

И кто не может устоять перед безумием Linq!

string names = "Joe,Bob,Lucy";
var myVars = names.Split(',').Select((x, index) => Tuple.Create(index,x)).ToDictionary(x => "var" + x.Item1, y => y.Item2);
Debug.WriteLine(myVars["var0"]);

Ответ 4

Не однострочное решение. Но как насчет метода расширения с дополнительным параметром out?

public static IEnumerable<T> Unpack<T>(this IEnumerable<T> source, out T target)
{
    target = source.First();
    return source.Skip(1);
}

Затем вы можете использовать метод, подобный этому.

string values = "aaa,bbb,ccc";
string x, y, z;
values.Split(',')
    .Unpack(out x)
    .Unpack(out y)
    .Unpack(out z);

Обратите внимание, что метод Unpack перечисляет последовательность дважды. Поэтому я бы использовал его только в том случае, если данные в объекте IEnumerable повторяемы.

Мне не хотелось проверять производительность метода, потому что я думал, что обычно мы не будем распаковывать большой массив.

Конечно, вы можете использовать модификатор ref вместо out, и использование будет отличаться.

Ответ 5

Я рекомендую словарь.

List<string> names = new List<string> (){"varA","varB","varC"};
Dictionary<string, string> map = new Dictionary<string, string>();
string[] results = myLongString.Split(',');
for (int i = 0; i < results.Length; i++) map.Add(names[i], results[i]); 

Ответ 6

@pnvn имеет отличную идею с шаблоном Unpack, но его можно было бы улучшить, чтобы перебирать перечисление за один проход, предоставлять значения по умолчанию за конец перечислимого и работать над любым типом или размером перечислимого в предсказуемом виде.

Ниже приведен пример использования функции С# 7 out variables.

"A,B,C".Split(',')
    .Unpack(out string varA)
    .Unpack(out string varB);

Для этого требуется два метода расширения: один для запуска IEnumerable, второй для IEnumerator для каждого последующего вызова.

public static IEnumerator<T> Unpack<T>(this IEnumerable<T> source, out T item)
{
    var enumerator = source.GetEnumerator();
    if (enumerator.MoveNext())
    {
        item = enumerator.Current;
        return enumerator;
    }
    item = default(T);
    return null;
}

public static IEnumerator<T> Unpack<T>(this IEnumerator<T> enumerator, out T item)
{
    if (enumerator != null && enumerator.MoveNext())
    {
        item = enumerator.Current;
        return enumerator;
    }            
    item = default(T);
    return null;
}

Ответ 7

В С# нет встроенного способа для выполнения множественного присваивания, как вы можете в Perl; однако существует множество решений для получения данных в каждой переменной по нормальному пути.

Ответ 8

Я не мог удержаться от добавления к смехотворности:) Пожалуйста, не используйте это "решение", по крайней мере, не как-есть.

static class StringExtensions
{
   private static readonly Func<object, string, Action<object, object>> GetSetter =
       (o1, n) => (o2, v) => o1.GetType().GetProperty(n).SetValue(o2, v, null);

   public static T AssignFromCSV<T>(this string csv, T obj, string[] propertyNames)
   {
       var a = csv.Split(new[] {','});
       for (var i = 0 ; i < propertyNames.Length; i++)
       {
           GetSetter(obj, propertyNames[i])(obj, a[i]);
       }
       return obj ;
   }
}

class TargetClass
{
   public string A { get; set; }

   public string B { get; set; }

   public string C { get; set; }
}

Использование:

var target = "1,2,3".AssignFromCSV(new TargetClass(), new[] {"A", "B", "C"}) ;