I have a string with several fields separated by a specific character, something like this:
我有一个字符串,其中有几个字段由特定字符分隔,如下所示:
A,B,C
A,B,C
I want to split the string at the commas and assign each resulting field to its own string variable. In Perl I can do that elegantly like this:
我想在逗号中拆分字符串并将每个结果字段分配给它自己的字符串变量。在Perl中我可以像这样优雅地做到这一点:
my ($varA, $varB, $varC) = split (/,/, $string);
What is the simplest and most elegant way to achieve the same result in C#?
在C#中实现相同结果的最简单,最优雅的方法是什么?
I know that I can split into an array:
我知道我可以分成一个数组:
string[] results = string.Split(',');
But then I would have to access the fields via their index, e.g. results[2]. That is difficult to read and error-prone - consider not having 3 buth 30 fields. For that reason I prefer having each field value in its own named variable.
但是,我必须通过他们的索引访问这些字段,例如结果[2]。这是难以阅读和容易出错的 - 考虑没有3个但是30个字段。出于这个原因,我更喜欢将每个字段值放在自己的命名变量中。
8 个解决方案
#1
8
I agree. Hiding the split in an Adapter class seems like a good approach and communicates your intent rather well:
我同意。隐藏在Adapter类中的拆分似乎是一种很好的方法,并且很好地传达了您的意图:
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
7
You can use Tuples (added in .Net 4). Tuples in MSDN
您可以使用元组(在.Net 4中添加)。 MSDN中的元组
This:
这个:
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; }
}
Could be achieved with something like this:
可以用这样的东西来实现:
public Tuple<string,string> SplitIntoVars(string toSplit)
{
string[] split = toSplit.Split(',');
return Tuple.Create(split[0],split[1]);
}
With a Tuple you can use:
使用元组你可以使用:
var x = SplitIntoVars(arr);
// you can access the items like this: x.Item1 or x.Item2 (and x.Item3 etc.)
You can also create a Tuple for using Tuple<string,int>
etc.
您还可以使用Tuple
Also... I don't really like out parameters, so you emulate returning multiple values using a Tuple (and obviously, also of varying types). this:
另外......我真的不喜欢参数,所以你模拟使用元组返回多个值(显然,也有不同的类型)。这个:
public void SplitIntoVariables(string input, out a, out b, out c)
{
string pieces[] = input.Split(',');
a = pieces[0];
b = pieces[1];
c = pieces[2];
}
turns into this:
变成这样:
public Tuple<string,string,string> SplitIntoVariables(string[] input)
{
string pieces[] = input.Split(',');
return Tuple.Create(pieces[0],pieces[1],pieces[2]);
}
Other (more imaginative) options could be creating an ExpandoObject (dynamic) that holds your values (something akin to ViewBag in ASP.NET MVC)
其他(更富有想象力)的选项可能是创建一个包含你的值的ExpandoObject(动态)(类似于ASP.NET MVC中的ViewBag)
#3
3
And who can't resist some Linq insanity!
谁也无法抗拒一些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
2
Not a one line solution. But, how about an extension method with an additional out
parameter?
不是一线解决方案。但是,带有额外输出参数的扩展方法怎么样?
public static IEnumerable<T> Unpack<T>(this IEnumerable<T> source, out T target)
{
target = source.First();
return source.Skip(1);
}
Then, you could use the method like this.
然后,您可以使用这样的方法。
string values = "aaa,bbb,ccc";
string x, y, z;
values.Split(',')
.Unpack(out x)
.Unpack(out y)
.Unpack(out z);
Note that the Unpack
method enumerates the sequence twice. So, I'd use it only if the data in the IEnumerable
object is repeatable.
请注意,Unpack方法枚举序列两次。所以,我只有在IEnumerable对象中的数据是可重复的时才使用它。
I didn't care to check the performance of the method because I thought that normally we would not unpack a large array.
我不关心检查方法的性能,因为我认为通常我们不会解压缩大型数组。
Of course, you could use ref
modifier instead of out
, and the usage would be different.
当然,您可以使用ref修饰符而不是out,并且用法会有所不同。
#5
1
I recommend a dictionary.
我推荐一本字典。
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
1
@pnvn has a great idea with the Unpack pattern, but it could be improved to iterate over the enumeration in a single pass, provide defaults past the end of the enumerable and work on any type or size of enumerable in a predictable way.
@pnvn对Unpack模式有一个好主意,但可以改进它在一次传递中迭代枚举,提供超过可枚举末尾的默认值,并以可预测的方式处理任何类型或大小的可枚举。
Here is an example using C# 7 out variables
feature.
以下是使用C#7输出变量功能的示例。
"A,B,C".Split(',')
.Unpack(out string varA)
.Unpack(out string varB);
This requires two extension methods, one for the IEnumerable to start, the second on the IEnumerator for each subsequent call.
这需要两个扩展方法,一个用于启动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
0
There is no built in way in C# to do a multiple assignment like you can in Perl; however, there are multiple solutions to get the data into each variable via a normal path.
在C#中没有内置的方法可以像在Perl中那样进行多重赋值;但是,有多种解决方案可以通过正常路径将数据输入每个变量。
#8
0
I couldn't resist adding to the ridiculousness :) Please don't use this "solution", at least not as-is.
我无法抗拒加入荒谬:)请不要使用这个“解决方案”,至少不是原样。
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; }
}
Usage:
用法:
var target = "1,2,3".AssignFromCSV(new TargetClass(), new[] {"A", "B", "C"}) ;
#1
8
I agree. Hiding the split in an Adapter class seems like a good approach and communicates your intent rather well:
我同意。隐藏在Adapter类中的拆分似乎是一种很好的方法,并且很好地传达了您的意图:
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
7
You can use Tuples (added in .Net 4). Tuples in MSDN
您可以使用元组(在.Net 4中添加)。 MSDN中的元组
This:
这个:
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; }
}
Could be achieved with something like this:
可以用这样的东西来实现:
public Tuple<string,string> SplitIntoVars(string toSplit)
{
string[] split = toSplit.Split(',');
return Tuple.Create(split[0],split[1]);
}
With a Tuple you can use:
使用元组你可以使用:
var x = SplitIntoVars(arr);
// you can access the items like this: x.Item1 or x.Item2 (and x.Item3 etc.)
You can also create a Tuple for using Tuple<string,int>
etc.
您还可以使用Tuple
Also... I don't really like out parameters, so you emulate returning multiple values using a Tuple (and obviously, also of varying types). this:
另外......我真的不喜欢参数,所以你模拟使用元组返回多个值(显然,也有不同的类型)。这个:
public void SplitIntoVariables(string input, out a, out b, out c)
{
string pieces[] = input.Split(',');
a = pieces[0];
b = pieces[1];
c = pieces[2];
}
turns into this:
变成这样:
public Tuple<string,string,string> SplitIntoVariables(string[] input)
{
string pieces[] = input.Split(',');
return Tuple.Create(pieces[0],pieces[1],pieces[2]);
}
Other (more imaginative) options could be creating an ExpandoObject (dynamic) that holds your values (something akin to ViewBag in ASP.NET MVC)
其他(更富有想象力)的选项可能是创建一个包含你的值的ExpandoObject(动态)(类似于ASP.NET MVC中的ViewBag)
#3
3
And who can't resist some Linq insanity!
谁也无法抗拒一些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
2
Not a one line solution. But, how about an extension method with an additional out
parameter?
不是一线解决方案。但是,带有额外输出参数的扩展方法怎么样?
public static IEnumerable<T> Unpack<T>(this IEnumerable<T> source, out T target)
{
target = source.First();
return source.Skip(1);
}
Then, you could use the method like this.
然后,您可以使用这样的方法。
string values = "aaa,bbb,ccc";
string x, y, z;
values.Split(',')
.Unpack(out x)
.Unpack(out y)
.Unpack(out z);
Note that the Unpack
method enumerates the sequence twice. So, I'd use it only if the data in the IEnumerable
object is repeatable.
请注意,Unpack方法枚举序列两次。所以,我只有在IEnumerable对象中的数据是可重复的时才使用它。
I didn't care to check the performance of the method because I thought that normally we would not unpack a large array.
我不关心检查方法的性能,因为我认为通常我们不会解压缩大型数组。
Of course, you could use ref
modifier instead of out
, and the usage would be different.
当然,您可以使用ref修饰符而不是out,并且用法会有所不同。
#5
1
I recommend a dictionary.
我推荐一本字典。
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
1
@pnvn has a great idea with the Unpack pattern, but it could be improved to iterate over the enumeration in a single pass, provide defaults past the end of the enumerable and work on any type or size of enumerable in a predictable way.
@pnvn对Unpack模式有一个好主意,但可以改进它在一次传递中迭代枚举,提供超过可枚举末尾的默认值,并以可预测的方式处理任何类型或大小的可枚举。
Here is an example using C# 7 out variables
feature.
以下是使用C#7输出变量功能的示例。
"A,B,C".Split(',')
.Unpack(out string varA)
.Unpack(out string varB);
This requires two extension methods, one for the IEnumerable to start, the second on the IEnumerator for each subsequent call.
这需要两个扩展方法,一个用于启动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
0
There is no built in way in C# to do a multiple assignment like you can in Perl; however, there are multiple solutions to get the data into each variable via a normal path.
在C#中没有内置的方法可以像在Perl中那样进行多重赋值;但是,有多种解决方案可以通过正常路径将数据输入每个变量。
#8
0
I couldn't resist adding to the ridiculousness :) Please don't use this "solution", at least not as-is.
我无法抗拒加入荒谬:)请不要使用这个“解决方案”,至少不是原样。
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; }
}
Usage:
用法:
var target = "1,2,3".AssignFromCSV(new TargetClass(), new[] {"A", "B", "C"}) ;