一种反序列化字符串内容的方法
本文关键字:方法 字符串 反序列化 一种 | 更新日期: 2023-09-27 18:28:38
我从一个文本文件中读取了一个自定义格式的字符串,该文本文件中多次出现模板实例。
澄清
我有一个字符串模板
--------------------
Id : {0}
Value : {1}
--------------------
我已经阅读了一个文本文件,其内容如下
--------------------
Id : 21
Value : Some Value 1
--------------------
--------------------
Id : 200
Value : Some Value 2
--------------------
--------------------
Id : 1
Value : Some Value 3
--------------------
--------------------
Id : 54
Value : Some Value 4
--------------------
我有class A
,它有两个公共属性Id和值
class A
{
public string Id { get; set; }
public string Value { get; set; }
}
是否可以将从文本文件读取的整个文本反序列化为List<A>
。
没有"for"、"foreach"或"while"循环的方法会更好。
我已经解析这样的文本文件40年了。他是最好的方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
enum State
{
FIND_ID,
FIND_VALUE
}
const string FILENAME = @"c:'temp'test.txt";
static void Main(string[] args)
{
List<A> a_s = new List<A>();
string inputLine = "";
StreamReader reader = new StreamReader(FILENAME);
State state = State.FIND_ID;
A a = null;
while ((inputLine = reader.ReadLine()) != null)
{
inputLine = inputLine.Trim();
if (!inputLine.StartsWith("-") && inputLine.Length > 0)
{
switch (state)
{
case State.FIND_ID :
if (inputLine.StartsWith("Id"))
{
string[] inputArray = inputLine.Split(new char[] { ':' });
a = new A();
a_s.Add(a);
a.Id = inputArray[1].Trim();
state = State.FIND_VALUE;
}
break;
case State.FIND_VALUE:
if (inputLine.StartsWith("Value"))
{
string[] inputArray = inputLine.Split(new char[] { ':' });
a.Value = inputArray[1].Trim();
state = State.FIND_ID;
}
break;
}
}
}
}
}
class A
{
public string Id { get; set; }
public string Value { get; set; }
}
}
如果您可以修改您的A类,使其具有如下构造函数:
class A
{
public string Id { get; set; }
public string Value { get; set; }
public A() { }
public A(string s)
{
string[] vals = s.Split((new string[] { "'r'n" }), StringSplitOptions.RemoveEmptyEntries);
this.Id = vals[0].Replace("Id : ", string.Empty).Trim();
this.Value = vals[1].Replace("Value : ", string.Empty).Trim();
}
// only overridden here for printing
public override string ToString()
{
return string.Format("Id : {0}'r'nValue : {1}'r'n", this.Id, this.Value);
}
}
您可以实现以下内容:
public static List<A> GetValues(string file)
{
List<string> vals = new List<string>(Regex.Split(System.IO.File.ReadAllText(file), "--------------------"));
vals.RemoveAll(delegate(string s) { return string.IsNullOrEmpty(s.Trim()); });
List<A> ret = new List<A>();
vals.ForEach(delegate(string s) { ret.Add(new A(s)); });
return ret;
}
public static void Main()
{
foreach (A a in GetValues(@"C:'somefile.txt")) {
Console.WriteLine(a);
}
}
你最初提出的问题是为了避免循环;它没有显式的循环构造(for
、foreach
、do/while
),但底层代码确实循环(例如,Regex.Split
、vals.RemoveAll
和vals.ForEach
都是循环),因此正如注释所指出的,在这种情况下,您无法真正避免循环。
需要注意的是,在一些基准测试之后,如果要读取的文件的格式与您指定的格式完全相同,那么这种方法的速度会惊人地快。作为比较,我创建了一个文件,并复制/粘贴了你的示例模板(你发布的4个结果),总共有1032个结果,文件大小约为75k,XML文件的结果约为65k(由于---
的文本较少),我编写了以下基准测试来运行:
public class A
{
public string Id { get; set; }
public string Value { get; set; }
public A() { }
public A(string s)
{
string[] vals = s.Split((new string[] { "'r'n" }), StringSplitOptions.RemoveEmptyEntries);
this.Id = vals[0].Replace("Id : ", string.Empty).Trim();
this.Value = vals[1].Replace("Value : ", string.Empty).Trim();
}
public A(string id, string val)
{
this.Id = id;
this.Value = val;
}
// only overridden here for printing
public override string ToString()
{
return string.Format("Id : {0}'r'nValue : {1}'r'n", this.Id, this.Value);
}
}
public static List<A> GetValuesRegEx(string file)
{
List<string> vals = new List<string>(Regex.Split(System.IO.File.ReadAllText(file), "--------------------"));
vals.RemoveAll(delegate(string s) { return string.IsNullOrEmpty(s.Trim()); });
List<A> ret = new List<A>();
vals.ForEach(delegate(string s) { ret.Add(new A(s)); });
return ret;
}
public static List<A> GetValuesXml(string file)
{
List<A> ret = new List<A>();
System.Xml.Serialization.XmlSerializer srl = new System.Xml.Serialization.XmlSerializer(ret.GetType());
System.IO.FileStream f = new System.IO.FileStream(file,
System.IO.FileMode.OpenOrCreate,
System.IO.FileAccess.ReadWrite,
System.IO.FileShare.ReadWrite);
ret = ((List<A>)srl.Deserialize(f));
f.Close();
return ret;
}
public static List<A> GetValues(string file)
{
List<A> ret = new List<A>();
List<string> vals = new List<string>(System.IO.File.ReadAllLines(file));
for (int i = 0; i < vals.Count; ++i) {
if (vals[i].StartsWith("---") && ((i + 3) < vals.Count) && (vals[i + 3].StartsWith("---"))) {
ret.Add(new A(vals[i + 1].Replace("Id : ", string.Empty), vals[i + 2].Replace("Value : ", string.Empty)));
i += 3;
}
}
return ret;
}
public static List<A> GetValuesStream(string file)
{
List<A> ret = new List<A>();
string line = "";
System.IO.StreamReader reader = new System.IO.StreamReader(file);
int state = 0;
A a = null;
while ((line = reader.ReadLine()) != null) {
line = line.Trim();
if (!line.StartsWith("-") || line.Length > 0) {
switch (state) {
case 0:
if (line.StartsWith("Id")) {
string[] inputArray = line.Split(new char[] { ':' });
a = new A();
ret.Add(a);
a.Id = inputArray[1].Trim();
state = 1;
}
break;
case 1:
if (line.StartsWith("Value")) {
string[] inputArray = line.Split(new char[] { ':' });
a.Value = inputArray[1].Trim();
state = 0;
}
break;
}
}
}
return ret;
}
public static void Main()
{
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
for (int x = 0; x < 5; ++x) {
double avg = 0d;
for (int i = 0; i < 100; ++i) {
sw.Restart();
List<A> txt = GetValuesRegEx(@"C:'somefile.txt");
sw.Stop();
avg += sw.Elapsed.TotalSeconds;
}
Console.WriteLine(string.Format("avg: {0} s", (avg / 100)));
// best out of 5: 0.002380452 s
avg = 0d;
sw.Stop();
for (int i = 0; i < 100; ++i) {
sw.Restart();
List<A> txt = GetValuesXml(@"C:'somefile.xml");
sw.Stop();
avg += sw.Elapsed.TotalSeconds;
}
Console.WriteLine(string.Format("avg: {0} s", (avg / 100)));
// best out of 5: 0.002042312 s
avg = 0d;
sw.Stop();
for (int i = 0; i < 100; ++i) {
sw.Restart();
List<A> xml = GetValues(@"C:'somefile.xml");
sw.Stop();
avg += sw.Elapsed.TotalSeconds;
}
Console.WriteLine(string.Format("avg: {0} s", (avg / 100)));
// best out of 5: 0.001148025 s
avg = 0d;
sw.Stop();
for (int i = 0; i < 100; ++i) {
sw.Restart();
List<A> txt = GetValuesStream(@"C:'somefile.txt");
sw.Stop();
avg += sw.Elapsed.TotalSeconds;
}
Console.WriteLine(string.Format("avg: {0} s", (avg / 100)));
// best out of 5: 0.002459861 s
avg = 0d;
sw.Stop();
}
sw.Stop();
}
为了清晰起见,以下是在英特尔i7@2.2 GHz和5400 RPM HDD(碎片约0.1%)上运行时的结果:
GetValuesRegEx
运行时间5次运行中的最佳平均值:0.002380452 s
GetValuesXml
运行时间5次运行中的最佳平均值:0.002042312 s
GetValues
(ReadAllLines
/loop)运行时间5次运行中的最佳平均值:0.001148025s
GetValuesStream
(StreamReader
/loop)运行时间5次运行中的最佳平均值:0.002459861 s
您的结果可能会有所不同,并且这没有考虑任何错误处理,因此在使用代码时需要将其考虑在内。
希望这能有所帮助。