我当前的项目处理大量传入的无线电消息(每天约5M),表示为字符串,然后必须将其分成预定大小的块,准备存储。
例如,消息将采用以下格式:
mzIIIICCssss
每个不同的char代表一个块,所以这个例子包含5个块(m,z,IIII,CC,ssss)。
使用该格式的消息示例可以是:
.91234NE0001
(。,9,1234,NE,0001)
到目前为止,我已经使用过substring
,但有人告诉我这不像正则表达式那样有效。如果是这种情况,我如何使用regex
匹配char位置而不是语义模式?
答案 0 :(得分:5)
Substring
比正则表达式快得多。由于您要做的就是将字符串分成固定大小的块,只需使用Substring
。
cHao的评论给了我另一个想法。你可以使用string(char[], int, int)
构造函数,有点像这样:
string message = ".91234NE0001";
char[] messageArr = message.ToCharArray();
string chunk1 = new string(messageArr, 0, 1);
string chunk2 = new string(messageArr, 1, 1);
string chunk3 = new string(messageArr, 2, 4);
string chunk4 = new string(messageArr, 6, 2);
string chunk5 = new string(messageArr, 8, 4);
你可以给变量更好的名字:)
这是执行Substring
正在做的事情的手动方式。我认为它会比Substring
方法更快,但我之前想到了错误的方法。它的速度可能大致相同。
答案 1 :(得分:3)
我认为,如果不放弃本机代码,您将能够实现最高效的是使用不安全的代码。
private static IEnumerable<string> ExtractChunksUnsafe(string format, string data)
{
if(format.Length != data.Length)
throw new ArgumentException("Format length must match Data length");
if(data.Length == 0)
throw new ArgumentException("Invalid Data length");
char prevFormat = '\0';
char currentFormat = format[0];
var chunks = new List<string>();
var builder = new StringBuilder();
unsafe
{
fixed(char * indexer = data)
{
var index = -1;
while(data.Length > ++index)
{
prevFormat = currentFormat;
currentFormat = format[index];
if(currentFormat != prevFormat)
{
chunks.Add(builder.ToString());
builder.Clear();
}
builder.Append((*(indexer + index)));
}
chunks.Add(builder.ToString());
builder.Clear();
}
}
return chunks;
}
比较:
private static IEnumerable<string> ExtractChunks(string format, string data)
{
if(format.Length != data.Length)
throw new ArgumentException("Format length must match Data length");
if(data.Length == 0)
throw new ArgumentException("Invalid Data length");
char prevFormat = '\0';
char currentFormat = format[0];
var prevIndex = 0;
var index = 1;
var message = data.ToCharArray();
var chunks = new List<string>();
while(data.Length > index)
{
prevFormat = currentFormat;
currentFormat = format[index];
if(currentFormat != prevFormat)
{
chunks.Add(new string(message, prevIndex, index - prevIndex));
prevIndex = index;
}
index++;
}
chunks.Add(new string(message, prevIndex, index - prevIndex));
return chunks;
}
样品:
string format = "mzIIIICCssss";
string data = ".a9876NE9001";
var chunks = ExtractChunks(format, data);
foreach(var message in chunks)
{
Console.WriteLine(message);
}
基准:
string format = "mzIIIICCssss";
string data = ".a9876NE9001";
// Warmup CLR
ExtractChunksUnsafe(format, data);
ExtractChunks(format, data);
TimeSpan unsafeCode;
TimeSpan safeCode;
var timer = Stopwatch.StartNew();
for(int i = 0; i < 10000000; i++)
{
ExtractChunksUnsafe(format, data);
}
unsafeCode = timer.Elapsed;
timer.Restart();
for(int i = 0; i < 10000000; i++)
{
ExtractChunks(format, data);
}
safeCode = timer.Elapsed;
timer.Stop();
Console.WriteLine("Unsafe time {0}", unsafeCode);
Console.WriteLine("Safe time {0}", safeCode);
结果:
Unsafe time 00:00:04.8551136
Safe time 00:00:03.1786573
甚至修改不安全的身体:
unsafe
{
fixed(char * indexer = data)
{
var prevIndex = 0;
var index = 1;
while(data.Length > index)
{
prevFormat = currentFormat;
currentFormat = format[index];
if(currentFormat != prevFormat)
{
chunks.Add(new string(indexer, prevIndex, index - prevIndex));
prevIndex = index;
}
index++;
}
chunks.Add(new string(indexer, prevIndex, index - prevIndex));
}
}
仍会导致时间较慢Unsafe time 00:00:03.4565302
。
答案 2 :(得分:2)
忽略哪个解决方案最有效的问题,这里的正则表达式与问题中给出的格式相匹配(mzIIIICCssss
)
(?<m>.)(?<z>.)(?<IIII>.{4})(?<CC>.{2})(?<ssss>.{4})
这将捕获名为“m”的组中的一个字符,名为“z”的组中的下一个字符,名为“IIII”的组中的接下来的4个字符,“CC”中的下一个字符,以及下一个字符4在“ssss”中。
就性能而言,如果您现在拥有的代码速度不够快,并且通过分析它确定字符串处理是问题,那么请寻找更快的替换。