将子字符串直接解析为double
本文关键字:double 字符串 | 更新日期: 2023-09-27 18:29:27
如果我有一个像1 2 3
这样的字符串,并且我确定了包含double
的子字符串的位置,我如何直接从子字符串中解析它而不创建临时字符串?
例如,我可以执行System.Double.Parse(str.Substring(0, 1))
,但这将创建一个缓慢且不必要的临时字符串。是否可以直接从原始字符串的一部分解析double?
编辑
埃里克·利珀特(Eric Lippert)对我在这里的动机提出了质疑,他说"小绳子很便宜"。这样做的动机来自于我对int的解析做了同样的事情,并看到了巨大的性能改进,因为显然,小字符串并不那么便宜。
下面是一个函数,它通过临时字符串对int序列进行lexes处理:
let lex f (s: string) =
let rec inside i0 (s: string, i) =
if i = s.Length then
f (s.Substring(i0, i-i0) |> System.Int32.Parse)
else
let c = s.[i]
if '0'<=c && c<='9' then
inside i0 (s, i+1)
else
f (s.Substring(i0, i-i0) |> System.Int32.Parse)
outside (s, i)
and outside (s: string, i) =
if i < s.Length then
let c = s.[i]
if '0'<=c && c<='9' then
inside i (s, i)
else
outside (s, i+1)
outside (s, 0)
从一个字符串到lex 15625000 int需要2.4秒。
这里有一个避免临时字符串的版本:
let lex f (s: string) =
let rec inside n (s: string, i) =
if i = s.Length then f n else
let c = s.[i]
if '0'<=c && c<='9' then
inside (10*n + int c - int '0') (s, i+1)
else
f n
outside (s, i)
and outside (s: string, i) =
if i < s.Length then
let c = s.[i]
if '0'<=c && c<='9' then
inside 0 (s, i)
else
outside (s, i+1)
outside (s, 0)
这需要0.255秒,比使用临时字符串的解决方案快9倍多!
我看不出为什么lexing float会有什么不同。因此,不提供从子字符串解析浮点值的能力。NET在表上留下了一个数量级的性能。我做了很多科学计算,经常需要处理大量数据,尤其是在刚开始的时候,所以我真的不想像这样把性能抛到九霄云外。
是的,我认为这是完全可行的。您可以编写自己的函数来进行解析,甚至可以基于Double.Parse()
的实际源代码进行解析。这段代码看起来并不庞大和可怕,我认为您可以根据自己的需要对其进行更多的优化。
您可以逐位解析字符串,如下所示:
static double CustomConvertToDouble(string input, int startIndex, int length)
{
double result = 0d;
int lastDigitIndex = startIndex + length - 1;
int power = 0;
for (int i = lastDigitIndex; i >= startIndex; i--)
{
int digit = (input[i] - '0');
result += (Math.Pow(10, power++)) * digit;
}
return result;
}
用法:
string tmp = "1 2 3";
double result = CustomConvertToDouble(tmp, 0, 1);
Console.WriteLine(result); // 1
你可以对此进行扩展,将小数点等因素考虑在内。
但我真的怀疑正常的方式是否会成为性能瓶颈,我很想知道你为什么要麻烦。如果这段代码真的对性能很关键,也许最好的方法是用另一种语言编写它?
如果你只寻找个位数,这很容易:
let readDigit s i =
let getDigit x =
if '0' <= x && x <= '9'
then byte x - 48uy // byte value of '0'
else failwith "Not a digit"
s |> Seq.item i |> getDigit |> double
该F#实现利用string
实现char seq
,并且char
值可以转换为byte
值。
不过,我怀疑它是否比使用Double.Parse(str.Substring(0, 1))
更快。
for (int x = 0; x < input.Length; x++)
{
if(input[x] != ' ')
Console.WriteLine(Double.Parse(input[x].ToString()));
}
不创建任何额外的可枚举对象,但Double.Parse只例外字符串,因此需要一个toString。
这是你能做的最好的
static void Main(string[] args)
{
string input = "1 2 3";
double[] output = input.Split(new char[] {' '},StringSplitOptions.RemoveEmptyEntries).Select(x => double.Parse(x)).ToArray();
}