我想解析一个包含十进制,八进制或十六进制格式的数字的字符串,我想用零堆分配来实现。因此正则表达式和子串出局。
基数由前缀确定:
1234
是十进制01234
是八进制
由于领先0x1234
(十进制4660)0x
为十六进制
十六进制值可能以0x
,0X
或0h
为前缀。 A
- F
个字符可能是大写或小写。
该方法应该有签名:
bool TryParse(string s, out int i)
该方法一旦到达s
的末尾,或者一旦到达空格,就应该停止解析。也就是说,以下所有内容应该产生相同的整数值1234:
"1234"
"1234 "
"0X4D2"
"0h4D2"
"0x4d2"
"0x4D2 "
"02322"
"02322 "
我打算自己开始编码,但是想把它放在那里,以防有人可以分享现有的解决方案。如果没有,我会在完成后分享我的解决方案。
答案 0 :(得分:2)
我最后编写了一个状态机,在一次传递中解析这些值,没有任何堆分配(据我所知)。
以下是方法:
public static bool TryParse(string s, out int i)
{
const int starting = 0;
const int negative = 1;
const int leadingZero = 2;
const int dec = 3;
const int oct = 4;
const int startingHex = 5;
const int hex = 6;
var state = starting;
var value = 0;
var isNegative = false;
// ReSharper disable once ForCanBeConvertedToForeach
for (var idx = 0; idx < s.Length; idx++)
{
var c = s[idx];
switch (state)
{
case starting:
case negative:
{
switch (c)
{
case '0':
state = leadingZero;
break;
case '-':
if (state == starting)
{
isNegative = true;
state = negative;
break;
}
i = default(int);
return false;
default:
if (char.IsDigit(c))
{
value = c - '0';
state = dec;
break;
}
if (state == starting && char.IsWhiteSpace(c))
break;
i = default(int);
return false;
}
break;
}
case leadingZero:
{
switch (c)
{
case 'x':
case 'X':
case 'h':
state = startingHex;
break;
case ' ':
i = 0;
return true;
default:
if (char.IsDigit(c))
{
value = c - '0';
state = oct;
break;
}
i = default(int);
return false;
}
break;
}
case dec:
{
if (char.IsDigit(c))
{
value *= 10;
value += c - '0';
continue;
}
if (char.IsWhiteSpace(c))
{
i = isNegative ? -value : value;
return true;
}
i = default(int);
return false;
}
case oct:
{
var v = c - '0';
if (v >= 0 && v < 8)
{
value *= 8;
value += v;
continue;
}
if (char.IsWhiteSpace(c))
{
i = isNegative ? -value : value;
return true;
}
i = default(int);
return false;
}
case hex:
case startingHex:
{
if (c >= '0' && c <= '9')
{
state = hex;
var v = c - '0';
value *= 16;
value += v;
continue;
}
var cl = char.ToLower(c);
if (cl >= 'a' && c <= 'f')
{
state = hex;
var v = cl - 'a' + 10;
value *= 16;
value += v;
continue;
}
if (state == hex && char.IsWhiteSpace(c))
{
i = isNegative ? -value : value;
return true;
}
i = default(int);
return false;
}
}
}
switch (state)
{
case dec:
case oct:
case hex:
i = isNegative ? -value : value;
return true;
case leadingZero:
i = 0;
return true;
default:
i = 0;
return false;
}
}
xUnit测试:
[Fact]
public void Parse()
{
void Test(string s, int expected)
{
Assert.True(ParseUtil.TryParse(s, out var actual));
Assert.Equal(expected, actual);
}
void TestFails(string s) => Assert.False(ParseUtil.TryParse(s, out var _));
Test("1234", 1234);
Test("1234 ", 1234);
Test("1234 ", 1234);
Test("0X4D2", 1234);
Test("0h4D2", 1234);
Test("0x4d2", 1234);
Test("0x4D2 ", 1234);
Test("02322", 1234);
Test("02322 ", 1234);
Test("002322 ", 1234);
Test("0002322 ", 1234);
Test("-1234", -1234);
Test("-1234 ", -1234);
Test("-1234 ", -1234);
Test("-0X4D2", -1234);
Test("-0h4D2", -1234);
Test("-0x4d2", -1234);
Test("-0x4D2 ", -1234);
Test("-02322", -1234);
Test("-02322 ", -1234);
Test("-002322 ", -1234);
Test("-0002322 ", -1234);
Test(" 1234", 1234);
Test(" 1234 ", 1234);
Test(" 1234 ", 1234);
Test(" 0X4D2", 1234);
Test(" 0h4D2", 1234);
Test(" 0x4d2", 1234);
Test(" 0x4D2 ", 1234);
Test(" 02322", 1234);
Test(" 02322 ", 1234);
Test(" 002322 ", 1234);
Test(" 0002322 ", 1234);
Test("0", 0);
Test("00", 0);
Test("000", 0);
Test("0x0", 0);
Test(" 0 ", 0);
Test(" 00 ", 0);
Test(" 000 ", 0);
Test(" 0x0 ", 0);
TestFails("Hello");
TestFails("0Hello");
TestFails("0xx1234");
TestFails("04D2");
TestFails("4D2");
TestFails("098LKJ");
TestFails("0x");
TestFails("0x ");
TestFails(" 0x ");
TestFails("- 123");
TestFails("--123");
}
希望它可以帮助其他人。