是否有东西能从字符串中的已知起始位置读取双精度?
我想避免double.Parse(s.Substring(...))
中的分配
分裂也很重要,有数字形式和数字。培养
感觉框架中必定存在某些东西,但我找不到它。
签名看起来像:
var d = double.Parse(text, startPos, numberstyles, formatProvider, out endPos);
答案 0 :(得分:0)
这是我写的,是的,它分配:
using System;
using System.Globalization;
internal static class DoubleParser
{
private const NumberStyles InvalidNumberStyles = ~(NumberStyles.AllowLeadingWhite |
NumberStyles.AllowTrailingWhite |
NumberStyles.AllowLeadingSign |
NumberStyles.AllowTrailingSign |
NumberStyles.AllowParentheses |
NumberStyles.AllowDecimalPoint |
NumberStyles.AllowThousands |
NumberStyles.AllowExponent |
NumberStyles.AllowCurrencySymbol |
NumberStyles.AllowHexSpecifier);
private const string LeadingSignNotAllowed = "Leading sign not allowed";
private const string ExponentNotAllowed = "Exponent not allowed";
private const string DecimalPointNotAllowed = "Decimal point not allowed";
internal static double Parse(
string text,
int start,
NumberStyles style,
IFormatProvider provider,
out int endPos)
{
if (!IsValidFloatingPointStyle(style))
{
throw new ArgumentException("Invalid NumberStyles", nameof(style));
}
if (style.HasFlag(NumberStyles.AllowHexSpecifier))
{
throw new ArgumentException("Hex not supported", nameof(style));
}
double result;
if (TryParse(text, start, style, provider, out result, out endPos))
{
return result;
}
var message = $"Expected to find a double starting at index {start}\r\n" +
$"String: {text}\r\n" +
$" {new string(' ', start)}^";
throw new FormatException(message);
}
internal static bool TryParse(
string text,
int start,
NumberStyles style,
IFormatProvider provider,
out double result,
out int endPos)
{
result = 0;
endPos = start;
if (string.IsNullOrEmpty(text))
{
return false;
}
if (!IsValidFloatingPointStyle(style))
{
return false;
}
if (style.HasFlag(NumberStyles.AllowHexSpecifier))
{
return false;
}
if (style.HasFlag(NumberStyles.AllowLeadingWhite))
{
ReadWhite(text, ref endPos);
}
if (char.IsWhiteSpace(text[endPos]))
{
return false;
}
if (TryParseDigits(text, endPos, style, provider, out result, out endPos))
{
return true;
}
if (provider == null)
{
provider = CultureInfo.CurrentCulture;
}
var format = NumberFormatInfo.GetInstance(provider);
if (TryRead(text, ref endPos, format.NaNSymbol))
{
result = double.NaN;
return true;
}
if (TryRead(text, ref endPos, format.PositiveInfinitySymbol))
{
result = double.PositiveInfinity;
return true;
}
if (TryRead(text, ref endPos, format.NegativeInfinitySymbol))
{
result = double.NegativeInfinity;
return true;
}
endPos = start;
return false;
}
// Try parse a double from digits ignoring +-Inf and NaN
private static bool TryParseDigits(
string text,
int start,
NumberStyles style,
IFormatProvider provider,
out double result,
out int end)
{
end = start;
var format = NumberFormatInfo.GetInstance(provider);
Sign sign;
if (TryReadSign(text, ref end, format, out sign))
{
if (!style.HasFlag(NumberStyles.AllowLeadingSign))
{
result = 0;
return false;
}
}
TryReadDigits(text, ref end);
if (TryRead(text, ref end, format.NumberDecimalSeparator))
{
if (!style.HasFlag(NumberStyles.AllowDecimalPoint))
{
result = 0;
return false;
}
TryReadDigits(text, ref end);
}
if (TryReadExponent(text, ref end))
{
if (!style.HasFlag(NumberStyles.AllowExponent))
{
result = 0;
return false;
}
TryReadSign(text, ref end, format, out sign);
if (TryReadDigits(text, ref end))
{
return TryParseSubString(text, start, ref end, style, provider, out result);
}
// This is a tricky spot we read digits followed by (sign) exponent
// then no digits were thrown. I choose to return the double here.
// Both alternatives will be wrong in some situations.
// returning false here would make it impossible to parse 1.2eV
var backStep = sign == Sign.None
? 1
: 2;
end -= backStep;
return TryParseSubString(text, start, ref end, style, provider, out result);
}
return TryParseSubString(text, start, ref end, style, provider, out result);
}
private static bool TryParseSubString(
string text,
int start,
ref int end,
NumberStyles style,
IFormatProvider provider,
out double result)
{
var s = text.Substring(start, end - start);
var success = double.TryParse(s, style, provider, out result);
if (!success)
{
end = start;
}
return success;
}
private static bool TryReadSign(string s,
ref int pos,
NumberFormatInfo format,
out Sign sign)
{
if (TryRead(s, ref pos, format.PositiveSign))
{
sign = Sign.Positive;
return true;
}
if (TryRead(s, ref pos, format.NegativeSign))
{
sign = Sign.Negative;
return true;
}
sign = Sign.None;
return false;
}
private static bool TryReadExponent(
string s,
ref int pos)
{
if (TryRead(s, ref pos, "E"))
{
return true;
}
if (TryRead(s, ref pos, "e"))
{
return true;
}
return false;
}
private static bool TryReadDigits(string s, ref int pos)
{
var start = pos;
while (pos < s.Length && char.IsDigit(s[pos]))
{
pos++;
}
return pos != start;
}
private static bool TryRead(string s, ref int pos, string toRead)
{
if (pos == s.Length)
{
return false;
}
int start = pos;
while (pos < s.Length &&
pos - start < toRead.Length)
{
if (s[pos] != toRead[pos - start])
{
pos = start;
return false;
}
pos++;
}
return true;
}
private static void ReadWhite(string s, ref int pos)
{
while (pos < s.Length && Char.IsWhiteSpace(s[pos]))
{
pos++;
}
}
private static bool IsValidFloatingPointStyle(NumberStyles style)
{
// Check for undefined flags
return (style & InvalidNumberStyles) == 0;
}
}
可悲的是,它分配了一个子字符串。 IIRC下一版本的C#将为子串提供更便宜的分配,直到那时所有令人讨厌的优化代码都被浪费了:) 它处理我能想到的测试用例。 只要它是有效的双倍,它就会读取。对于一般情况,问题是无法解决的,但它适用于我需要它。
也许这对某人有用。