我正在处理由作为字符串发送给我的逗号分隔的值。字符串有许多不同的结构(意味着字符串的不同位置中的不同数据类型以及不同数量的数据)。因此,虽然一个字符串可能表示为:
- 公共数据,标识符,整数,字符串,字符串,字符串。
另一个可能表示为:
- 公共数据,标识符,整数,整数,字符串,字符串,字符串。
设计目标:
是否有良好的设计模式或设计模式的组合,只有在拉入适量的值并且这些值是预期的数据类型时,才允许我解析值,检查它们并返回对象?
注意:我正在处理超过30种不同的字符串结构。
答案 0 :(得分:1)
只需使用string.Split()
分割它们,然后根据需要在结果数组中的每个int
int.Parse()
或int.TryParse()
分割它们。
var myStrings = string.Split(sourceString);
int myint1 = int.Parse(myStrings[0]);
答案 1 :(得分:1)
有几种方法可以解决这个问题。这是一个简单的(只输出一个对象数组):
class Template
{
// map identifiers to templates
static Dictionary<string, string> templates = new Dictionary<string, string>
{
{ "type1", "isss" },
{ "type2", "iisss" },
};
static bool ParseItem(string input, char type, out object output)
{
output = null;
switch (type)
{
case 'i':
int i;
bool valid = int.TryParse(input, out i);
output = i;
return valid;
case 's':
output = input;
return true;
}
return false;
}
public static object[] ParseString(string input)
{
string[] items = input.Split(',');
// make sure we have enough items
if (items.Length < 2)
return null;
object[] output = new object[items.Length - 2];
string identifier = items[1];
string template;
// make sure a valid identifier was specified
if (!templates.TryGetValue(identifier, out template))
return null;
// make sure we have the right amount of data
if (template.Length != output.Length)
return null;
// parse each item
for (int i = 0; i < template.Length; i++)
if (!ParseItem(items[i + 2], template[i], out output[i]))
return null;
return output;
}
}
答案 2 :(得分:1)
如果所有行都以common data, identifier
开头,然后是一个变量但预期(即基于标识符已知)的值集合,那么表格方法可以很好地工作。要继续您的示例,请说您有两种不同的类型:
您可以构建一个定义您要查找内容的类:
class ItemDesc
{
public string Ident { get; private set; }
public string Fields { get; private set; }
public ItemDesc(string id, string flds)
{
Ident = id;
Fields = flds;
}
}
Fields
属性只是一个字符串,其中包含可变数据的单字符类型描述。也就是说,“isss”将被解释为int,string,string,string
。
然后,您可以构建一个Dictionary<string, ItemDesc>
,您可以使用它来查看这些内容:
Dictionary<string, ItemDesc> ItemLookup = new Dictionary<string, ItemDesc>
{
{ "ItemType1", new ItemDesc("ItemType1", "isss") },
{ "ItemType2", new ItemDesc("ItemType2", "iisss") },
};
现在,当您阅读一行时,请使用string.Split()
将其拆分为字段。获取标识符,查找字典以获取项目描述,然后解析其余字段。类似的东西:
string line = GetLine();
var fields = line.Split(',');
// somehow get the identifier
string id = GetIdentifier();
ItemDesc desc;
if (!ItemLookup.TryGetValue(id, out desc))
{
// unrecognized identifier
}
else
{
int fieldNo = 3; // or whatever field is after the identifier
foreach (var c in desc.Fields)
{
switch (c)
{
case 'i' :
// try to parse an int and save it.
break;
case 's' :
// save the string
break;
default:
// error, unknown field type
break;
}
++fieldNo;
}
}
// at this point if no errors occurred, then you have a collection
// of parsed fields that you saved. You can now create your object.
答案 3 :(得分:0)
需要更多细节,根据您的问题域,它可能会完全改变。但是以下似乎是第一组模式,它们是按照适用性排序的。
答案 4 :(得分:0)
如果您对返回实际对象而不仅仅是对象数组感兴趣,可以将元数据放入要返回的对象的类定义中。然后,当您获得对象类型时,您将查找元数据以确定在输入数组中找到其值的位置。这是一个简单的例子:
namespace Parser
{
// create metadata attribute
class CsvPositionAttribute : Attribute
{
public int Position { get; set; }
public CsvPositionAttribute(int position)
{
Position = position;
}
}
// define some classes that use our metadata
public class type1
{
[CsvPosition(0)]
public int int1;
[CsvPosition(1)]
public string str1;
[CsvPosition(2)]
public string str2;
[CsvPosition(3)]
public string str3;
}
public class type2
{
[CsvPosition(0)]
public int int1;
[CsvPosition(1)]
public int int2;
[CsvPosition(2)]
public string str1;
[CsvPosition(3)]
public string str2;
[CsvPosition(4)]
public string str3;
}
public class CsvParser
{
public static object ParseString(string input)
{
string[] items = input.Split(',');
// make sure we have enough items
if (items.Length < 2)
return null;
string identifier = items[1];
// assume that our identifiers refer to a type in our namespace
Type type = Type.GetType("Parser." + identifier, false);
if (type == null)
return null;
object output = Activator.CreateInstance(type);
// iterate over fields in the type -- you may want to use properties
foreach (var field in type.GetFields())
// find the members that have our position attribute
foreach (CsvPositionAttribute attr in
field.GetCustomAttributes(typeof(CsvPositionAttribute),
false))
// if the item exists, convert it to the type of the field
if (attr.Position + 2 >= items.Length)
return null;
else
// ChangeType may throw exceptions on failure;
// catch them and return an error
try { field.SetValue(output,
Convert.ChangeType(items[attr.Position + 2],
field.FieldType));
} catch { return null; }
return output;
}
}
}