我正试图找到一种将消息字符串解析为对象的好方法。字符串的长度是固定的,如下所述。

做一个简单的String.Split是可行的,但是当您开始接近字符串的末尾时,我想可能会有些麻烦。例如:
var field1 = s.SubString(0,2);
var field2 = s.SubString(2,4);
....
var field99 = s.SubString(88,4); // difficult magic numbers我考虑使用Regex,并认为这可能更令人困惑。
我试图想出一个优雅的解决方案,在这里我可以创建一个Parser,它被传递给一个'config‘,它将详细说明如何解析字符串。
就像..。
MyConfig config = new MyConfig()
config.Add("Protocol", Length=2, typeof(int));
config.Add("MessageType", Length=1, typeof(char));
Parser p = new Parser(config);
var parserResult = p.Parse(message);...but,我一分钟就绕圈转,什么也没做。任何指点都会有很大帮助。
发布于 2017-01-25 11:29:53
我不认为如果这样做正确的话,判决是不会令人困惑的。您可以使用命名捕获组,并且可以非常整洁地定义它(例如,对于前三个字段,您可以任意扩展这些字段):
const string GRP_PROTOCOL = "protocol";
const string GRP_MESSAGE_TYPE = "msgtype";
const string GRP_MEASUREMENT = "measurement";
Regex parseRegex = new Regex(
$"(?<{GRP_PROTOCOL}>.{{2}})" +
$"(?<{GRP_MESSAGE_TYPE}>.{{1}})" +
$"(?<{GRP_MEASUREMENT}>.{{4}})");还可以在数组中定义组及其长度:
const string GRP_PROTOCOL = "protocol";
const string GRP_MESSAGE_TYPE = "msgtype";
const string GRP_MEASUREMENT = "measurement";
Tuple<string, int>[] groups = {
Tuple.Create( GRP_PROTOCOL, 2 ),
Tuple.Create( GRP_MESSAGE_TYPE, 1 ),
Tuple.Create( GRP_MEASUREMENT, 4 )
};
Regex parseRegex =
new Regex(String.Join("", groups.Select(grp => $"(?<{grp.Item1}>.{{{grp.Item2}}})").ToArray()));然后,您可以在需要时按名称访问组:
Match match = parseRegex.Match(message);
string protocol = match.Groups[GRP_PROTOCOL].Value;
string msgType = match.Groups[GRP_MESSAGE_TYPE].Value;
string measurement = match.Groups[GRP_MEASUREMENT].Value;发布于 2017-01-25 11:42:09
因此,一个简单的消息结构:
class Message
{
public DateTime DateTime { get; set; }
public int Protocol { get; set; }
public string Measurement { get; set; }
public string Type { get; set; }
//....
}与一个知道如何反序列化它的类组合在一起:
class MessageSerializer
{
public Message Deserialize(string str)
{
Message message = new Message();
int index = 0;
message.Protocol = DeserializeProperty(str, ref index, 2, Convert.ToInt32);
message.Type = DeserializeProperty(str, ref index, 1, Convert.ToString);
message.Measurement = DeserializeProperty(str, ref index, 4, Convert.ToString);
message.DateTime = DeserializeProperty<DateTime>(str, ref index, 16, (s) =>
{
// Parse date time from 2013120310:28:55 format
return DateTime.ParseExact(s, "yyyyMMddhh:mm:ss", CultureInfo.CurrentCulture);
});
//...
return message;
}
static T DeserializeProperty<T>(string str, ref int index, int count,
Func<string, T> converter)
{
T property = converter(str.Substring(index, count));
index += count;
return property;
}
}发布于 2017-01-25 11:49:15
如果输入字符串中的属性是固定宽度的,那么Regex在实现和性能方面都是开销。创建一个通用解析器的想法是好的,但是如果您有多个解析器要实现的话,它是有意义的。因此,如果只有一个特定的实现,就没有理由有抽象。
我会和StringReader一起去
using (var reader = new StringReader(input)) {
}然后,...and创建一些助手扩展方法,如下所示:
// just a sample code, to get the idea
public static string ReadString(this TextReader reader, int count)
{
var buffer = new char[count];
reader.Read(buffer, 0, count);
return string.Join(string.Empty, buffer);
}
public static int ReadNumeric(this TextReader reader, int count)
{
var str = reader.ReadString(count);
int result;
if (int.TryParse(str, out result))
{
return result;
}
// handle error
}
// ...最后的用法是这样的:
using (var reader = new StringReader(input)) {
var protocol = reader.ReadNumeric(2);
var messageType = reader.ReadString(1);
var measurement = reader.ReadString(4);
// ...
}https://stackoverflow.com/questions/41850029
复制相似问题