Start moving towards invariant integer number parsing

It turns out that some languages use a unicode character such as \u2212 for the negative sign
This commit is contained in:
UnknownShadow200 2023-12-20 18:45:36 +11:00
parent daf4f0c05a
commit c2efe55058
36 changed files with 151 additions and 97 deletions

View File

@ -36,9 +36,9 @@ namespace MCGalaxy.Bots
public override InstructionData Parse(string[] args) {
Coords coords;
coords.X = int.Parse(args[1]);
coords.Y = int.Parse(args[2]);
coords.Z = int.Parse(args[3]);
coords.X = NumberUtils.ParseInt32(args[1]);
coords.Y = NumberUtils.ParseInt32(args[2]);
coords.Z = NumberUtils.ParseInt32(args[3]);
coords.RotX = byte.Parse(args[4]);
coords.RotY = byte.Parse(args[5]);
@ -51,7 +51,8 @@ namespace MCGalaxy.Bots
w.WriteLine(Name + " " + p.Pos.X + " " + p.Pos.Y + " " + p.Pos.Z + " " + p.Rot.RotY + " " + p.Rot.HeadX);
}
protected struct Coords {
protected struct Coords
{
public int X, Y, Z;
public byte RotX, RotY;
}

View File

@ -692,7 +692,7 @@ namespace MCGalaxy.Commands.CPE
int max = Block.MaxRaw;
// Check for block names (can't use standard parsing behaviour)
if (!int.TryParse(arg, out raw)) {
if (!NumberUtils.TryParseInt32(arg, out raw)) {
BlockDefinition def = BlockDefinition.ParseName(arg, args.defs);
if (def == null) {

View File

@ -81,7 +81,7 @@ namespace MCGalaxy
protected static bool IsListModifier(string str) {
int ignored;
return str.CaselessEq("all") || int.TryParse(str, out ignored);
return str.CaselessEq("all") || NumberUtils.TryParseInt32(str, out ignored);
}
public static bool IsCreateAction(string str) {

View File

@ -98,7 +98,7 @@ namespace MCGalaxy.Commands
public static bool GetInt(Player p, string input, string argName, ref int result,
int min = int.MinValue, int max = int.MaxValue) {
int value;
if (!int.TryParse(input, out value)) {
if (!NumberUtils.TryParseInt32(input, out value)) {
p.Message("&W\"{0}\" is not a valid integer.", input); return false;
}
@ -110,7 +110,7 @@ namespace MCGalaxy.Commands
public static bool GetReal(Player p, string input, string argName, ref float result,
float min = float.NegativeInfinity, float max = float.MaxValue) {
float value;
if (!Utils.TryParseSingle(input, out value)) {
if (!NumberUtils.TryParseSingle(input, out value)) {
p.Message("&W\"{0}\" is not a valid number.", input); return false;
}
@ -268,8 +268,8 @@ namespace MCGalaxy.Commands
bits = input.Split(new char[] { '-' }, 2);
int tmp;
return int.TryParse(bits[0], out tmp)
&& int.TryParse(bits[1], out tmp);
return NumberUtils.TryParseInt32(bits[0], out tmp)
&& NumberUtils.TryParseInt32(bits[1], out tmp);
}
}
}

View File

@ -57,8 +57,11 @@ namespace MCGalaxy.Commands.Info
offset = 5;
} else {
// Backwards compatibility with old format
int min = int.Parse(args[2]), hour = int.Parse(args[3]);
int day = int.Parse(args[4]), month = int.Parse(args[5]), year = int.Parse(args[6]);
int min = NumberUtils.ParseInt32(args[2]);
int hour = NumberUtils.ParseInt32(args[3]);
int day = NumberUtils.ParseInt32(args[4]);
int month = NumberUtils.ParseInt32(args[5]);
int year = NumberUtils.ParseInt32(args[6]);
delta = DateTime.Now - new DateTime(year, month, day, hour, min, 0);
newRank = args[7]; oldRank = args[8];

View File

@ -45,7 +45,7 @@ namespace MCGalaxy.Commands.Moderation {
if (reason.Length == 0 || reason[0] != '@') return reason;
reason = reason.Substring(1);
if (!int.TryParse(reason, out ruleNum)) return "@" + reason;
if (!NumberUtils.TryParseInt32(reason, out ruleNum)) return "@" + reason;
// Treat @num as a shortcut for rule #num
Dictionary<int, string> sections = GetRuleSections();

View File

@ -31,7 +31,7 @@ namespace MCGalaxy.Commands.World {
if (message.Length > 0) {
string[] parts = message.SplitSpaces();
if (parts.Length == 1) {
if (!int.TryParse(parts[0], out seconds)) {
if (!NumberUtils.TryParseInt32(parts[0], out seconds)) {
seconds = 30;
lvl = Matcher.FindLevels(p, parts[0]);
if (lvl == null) return;

View File

@ -32,7 +32,7 @@ namespace MCGalaxy.Commands.Building
double n1 = 0, n2 = 0, result = 0;
string r1 = args[0], op = args[1], r2 = null, format = null;
if (!Utils.TryParseDouble(r1, out n1)) {
if (!NumberUtils.TryParseDouble(r1, out n1)) {
p.Message("&W\"{0}\" is not a valid number.", r1); return;
}
@ -41,7 +41,7 @@ namespace MCGalaxy.Commands.Building
if (args.Length == 2 ||op.Length > 1) { Help(p); return; }
r2 = args[2];
if (!Utils.TryParseDouble(r2, out n2)) {
if (!NumberUtils.TryParseDouble(r2, out n2)) {
p.Message("&W\"{0}\" is not a valid number.", r2); return;
}

View File

@ -69,7 +69,7 @@ namespace MCGalaxy.Commands.Building {
int value;
if (arg == "x" || arg == "y" || arg == "z") {
axis = char.ToUpper(arg[0]); return true;
} else if (int.TryParse(arg, out value)) {
} else if (NumberUtils.TryParseInt32(arg, out value)) {
// Clamp to [0, 360)
value %= 360;
if (value < 0) value += 360;

View File

@ -34,7 +34,7 @@ namespace MCGalaxy.Commands.Building {
if (tree == null) tree = new NormalTree();
int size;
if (args.Length > 1 && int.TryParse(args[1], out size)) {
if (args.Length > 1 && NumberUtils.TryParseInt32(args[1], out size)) {
Player p = dArgs.Player;
string opt = args[0] + " tree size";
if (!CommandParser.GetInt(p, args[1], opt, ref size, tree.MinSize, tree.MaxSize)) return null;

View File

@ -28,7 +28,7 @@ namespace MCGalaxy.Config
// separate function to avoid boxing in derived classes
protected int ParseInteger(string raw, int def, int min, int max) {
int value;
if (!int.TryParse(raw, out value)) {
if (!NumberUtils.TryParseInt32(raw, out value)) {
Logger.Log(LogType.Warning, "Config key \"{0}\" has invalid integer '{2}', using default of {1}", Name, def, raw);
value = def;
}
@ -101,7 +101,7 @@ namespace MCGalaxy.Config
protected double ParseReal(string raw, double def, double min, double max) {
double value;
if (!Utils.TryParseDouble(raw, out value)) {
if (!NumberUtils.TryParseDouble(raw, out value)) {
Logger.Log(LogType.Warning, "Config key \"{0}\" has invalid number '{2}', using default of {1}", Name, def, raw);
value = def;
}
@ -118,8 +118,8 @@ namespace MCGalaxy.Config
}
public override string Serialise(object value) {
if (value is float) return Utils.StringifyDouble((float)value);
if (value is double) return Utils.StringifyDouble((double)value);
if (value is float) return NumberUtils.StringifyDouble((float)value);
if (value is double) return NumberUtils.StringifyDouble((double)value);
return base.Serialise(value);
}
}

View File

@ -124,13 +124,13 @@ namespace MCGalaxy.Commands
// Old format - Name:Num : Lowest : Description
if (IsDescription(args[3])) {
min = (LevelPermission)int.Parse(args[2]);
min = (LevelPermission)NumberUtils.ParseInt32(args[2]);
allowed = null; disallowed = null;
} else {
Deserialise(args, 2, out min, out allowed, out disallowed);
}
perms = GetOrAdd(args[0], int.Parse(args[1]), min);
perms = GetOrAdd(args[0], NumberUtils.ParseInt32(args[1]), min);
perms.Init(min, allowed, disallowed);
} catch (Exception ex) {
Logger.Log(LogType.Warning, "Hit an error on the extra command perms " + line);

View File

@ -128,7 +128,7 @@ namespace MCGalaxy
protected static void Deserialise(string[] args, int idx, out LevelPermission min,
out List<LevelPermission> allowed,
out List<LevelPermission> disallowed) {
min = (LevelPermission)int.Parse(args[idx]);
min = (LevelPermission)NumberUtils.ParseInt32(args[idx]);
disallowed = ExpandPerms(args[idx + 1]);
allowed = ExpandPerms(args[idx + 2]);
}
@ -139,7 +139,7 @@ namespace MCGalaxy
List<LevelPermission> perms = new List<LevelPermission>();
foreach (string perm in input.SplitComma())
{
perms.Add((LevelPermission)int.Parse(perm));
perms.Add((LevelPermission)NumberUtils.ParseInt32(perm));
}
return perms;
}

View File

@ -234,8 +234,13 @@ namespace MCGalaxy.SQL
internal static TimeSpan ParseOldDBTimeSpent(string value) {
string[] parts = value.SplitSpaces();
return new TimeSpan(int.Parse(parts[0]), int.Parse(parts[1]),
int.Parse(parts[2]), int.Parse(parts[3]));
int days = NumberUtils.ParseInt32(parts[0]);
int hours = NumberUtils.ParseInt32(parts[1]);
int mins = NumberUtils.ParseInt32(parts[2]);
int secs = NumberUtils.ParseInt32(parts[3]);
return new TimeSpan(days, hours, mins, secs);
}
public static DateTime ParseDBDate(string value) {

View File

@ -141,7 +141,7 @@ namespace MCGalaxy.DB
}
internal static int ParseInt(string value) {
return (value.Length == 0 || value.CaselessEq("null")) ? 0 : int.Parse(value);
return (value.Length == 0 || value.CaselessEq("null")) ? 0 : NumberUtils.ParseInt32(value);
}
internal static string ParseColor(string raw) {

View File

@ -60,7 +60,7 @@ namespace MCGalaxy.Eco
public static string FindMatches(Player p, string name, out int money) {
string[] match = PlayerDB.MatchValues(p, name, "Name,Money");
money = match == null ? 0 : int.Parse(match[1]);
money = match == null ? 0 : NumberUtils.ParseInt32(match[1]);
return match == null ? null : match[0];
}

View File

@ -47,7 +47,7 @@ namespace MCGalaxy.Eco
if (prop.CaselessEq("enabled")) {
Enabled = value.CaselessEq("true");
} else if (prop.CaselessEq("purchaserank")) {
PurchaseRank = (LevelPermission)int.Parse(value);
PurchaseRank = (LevelPermission)NumberUtils.ParseInt32(value);
} else {
Parse(prop, value);
}

View File

@ -46,7 +46,7 @@ namespace MCGalaxy.Eco
if (perm == LevelPermission.Null) return;
RankEntry rank = GetOrAdd(perm);
rank.Price = int.Parse(args[1]);
rank.Price = NumberUtils.ParseInt32(args[1]);
}
public override void Serialise(List<string> cfg) {

View File

@ -69,7 +69,7 @@ namespace MCGalaxy
string str = sep == -1 ? null : model.Substring(sep + 1);
float scale;
if (!Utils.TryParseSingle(str, out scale)) scale = 1.0f;
if (!NumberUtils.TryParseSingle(str, out scale)) scale = 1.0f;
if (scale < 0.01f) scale = 0.01f;
// backwards compatibility for giant model

View File

@ -47,7 +47,7 @@ namespace MCGalaxy.Generator
if (ArgFilter(arg)) {
if (!ArgParser(arg)) return false;
} else if (int.TryParse(arg, out Seed)) {
} else if (NumberUtils.TryParseInt32(arg, out Seed)) {
gotSeed = true;
} else {
if (!CommandParser.GetEnum(p, arg, "Seed", ref Biome)) return false;
@ -87,7 +87,8 @@ namespace MCGalaxy.Generator
if (seed.Length == 0) return new Random();
int value;
if (!int.TryParse(seed, out value)) value = seed.GetHashCode();
if (!NumberUtils.TryParseInt32(seed, out value))
value = seed.GetHashCode();
return new Random(value);
} // TODO move to CmdMaze

View File

@ -82,7 +82,7 @@ namespace MCGalaxy {
if (!part.CaselessStarts("jumpheight=")) continue;
string heightPart = part.Substring(part.IndexOf('=') + 1);
float value;
if (Utils.TryParseSingle(heightPart, out value))
if (NumberUtils.TryParseSingle(heightPart, out value))
maxJump = (short)(value * 32);
}
return Packet.HackControl(fly, noclip, speed, respawn, thirdPerson, maxJump);

View File

@ -146,7 +146,7 @@ namespace MCGalaxy {
p.Message("Reset weather for {0} &Sto 0 (Sun)", area);
weather = EnvConfig.ENV_USE_DEFAULT;
} else {
if (int.TryParse(value, out weather)) {
if (NumberUtils.TryParseInt32(value, out weather)) {
} else if (value.CaselessEq("sun")) { weather = 0;
} else if (value.CaselessEq("rain")) { weather = 1;
} else if (value.CaselessEq("snow")) { weather = 2;

View File

@ -103,7 +103,7 @@ namespace MCGalaxy {
string backupName = BackupNameFrom(path);
int num;
if (!int.TryParse(backupName, out num)) continue;
if (!NumberUtils.TryParseInt32(backupName, out num)) continue;
latest = Math.Max(num, latest);
}
return latest;

View File

@ -48,7 +48,7 @@ namespace MCGalaxy
{
string restore = LevelInfo.BackupNameFrom(path);
int num;
if (int.TryParse(restore, out num)) continue;
if (NumberUtils.TryParseInt32(restore, out num)) continue;
count++;
custom.Append(", " + restore);

View File

@ -660,6 +660,7 @@
<Compile Include="Server\Maintenance\ZipWriter.cs" />
<Compile Include="util\Formatting\Wildcard.cs" />
<Compile Include="util\ImageUtils.cs" />
<Compile Include="util\NumberUtils.cs" />
<Compile Include="util\OperatingSystem.cs" />
<Compile Include="Server\Server.cs" />
<Compile Include="Server\Server.DB.cs" />

View File

@ -183,8 +183,8 @@ namespace MCGalaxy.Modules.Compiling
if (full) {
ce.FileName = m.Groups[2].Value;
ce.Line = int.Parse(m.Groups[4].Value, CultureInfo.InvariantCulture);
ce.Column = int.Parse(m.Groups[5].Value, CultureInfo.InvariantCulture);
ce.Line = NumberUtils.ParseInt32(m.Groups[4].Value);
ce.Column = NumberUtils.ParseInt32(m.Groups[5].Value);
}
ce.IsWarning = m.Groups[full ? 6 : 1].Value.CaselessEq("warning");

View File

@ -146,9 +146,9 @@ namespace MCGalaxy.Modules.Relay.Discord
string retryAfter = res.Headers["Retry-After"];
float delay;
if (Utils.TryParseSingle(resetAfter, out delay) && delay > 0) {
if (NumberUtils.TryParseSingle(resetAfter, out delay) && delay > 0) {
// Prefer Discord "X-RateLimit-Reset-After" (millisecond precision)
} else if (Utils.TryParseSingle(retryAfter, out delay) && delay > 0) {
} else if (NumberUtils.TryParseSingle(retryAfter, out delay) && delay > 0) {
// Fallback to general "Retry-After" header
} else {
// No recommended retry delay.. 30 seconds is a good bet

View File

@ -161,7 +161,7 @@ namespace MCGalaxy.Modules.Relay.Discord
JsonObject obj = (JsonObject)ctx.Parse();
if (obj == null) return;
int opcode = int.Parse((string)obj["op"]);
int opcode = NumberUtils.ParseInt32((string)obj["op"]);
DispatchPacket(opcode, obj);
}
@ -187,7 +187,7 @@ namespace MCGalaxy.Modules.Relay.Discord
void HandleHello(JsonObject obj) {
JsonObject data = (JsonObject)obj["d"];
string interval = (string)data["heartbeat_interval"];
int msInterval = int.Parse(interval);
int msInterval = NumberUtils.ParseInt32(interval);
heartbeat = Server.Heartbeats.QueueRepeat(SendHeartbeat, null,
TimeSpan.FromMilliseconds(msInterval));
@ -249,7 +249,7 @@ namespace MCGalaxy.Modules.Relay.Discord
obj["op"] = OPCODE_HEARTBEAT;
if (Session.LastSeq != null) {
obj["d"] = int.Parse(Session.LastSeq);
obj["d"] = NumberUtils.ParseInt32(Session.LastSeq);
} else {
obj["d"] = null;
}
@ -275,7 +275,7 @@ namespace MCGalaxy.Modules.Relay.Discord
{
{ "token", Token },
{ "session_id", Session.ID },
{ "seq", int.Parse(Session.LastSeq) }
{ "seq", NumberUtils.ParseInt32(Session.LastSeq) }
};
}

View File

@ -109,9 +109,9 @@ namespace MCGalaxy.Modules.Warps
try {
warp.Name = parts[0];
warp.Level = parts[1];
warp.Pos.X = int.Parse(parts[2]);
warp.Pos.Y = int.Parse(parts[3]);
warp.Pos.Z = int.Parse(parts[4]);
warp.Pos.X = NumberUtils.ParseInt32(parts[2]);
warp.Pos.Y = NumberUtils.ParseInt32(parts[3]);
warp.Pos.Z = NumberUtils.ParseInt32(parts[4]);
warp.Yaw = byte.Parse(parts[5]);
warp.Pitch = byte.Parse(parts[6]);
warps.Add(warp);

View File

@ -133,9 +133,12 @@ namespace MCGalaxy
string[] date = raw.SplitSpaces();
string[] minuteHour = date[5].Split(':');
int hour = int.Parse(minuteHour[0]), minute = int.Parse(minuteHour[1]);
int day = int.Parse(date[1]), month = int.Parse(date[2]), year = int.Parse(date[3]);
return new DateTime(year, month, day, hour, minute, 0).ToUniversalTime();
int hour = NumberUtils.ParseInt32(minuteHour[0]);
int min = NumberUtils.ParseInt32(minuteHour[1]);
int day = NumberUtils.ParseInt32(date[1]);
int month = NumberUtils.ParseInt32(date[2]);
int year = NumberUtils.ParseInt32(date[3]);
return new DateTime(year, month, day, hour, min, 0).ToUniversalTime();
}

View File

@ -194,9 +194,9 @@ namespace MCGalaxy
string modelScales = Server.modelScales.Get(name);
if (!string.IsNullOrEmpty(modelScales)) {
string[] bits = modelScales.SplitSpaces(3);
Utils.TryParseSingle(bits[0], out ScaleX);
Utils.TryParseSingle(bits[1], out ScaleY);
Utils.TryParseSingle(bits[2], out ScaleZ);
NumberUtils.TryParseSingle(bits[0], out ScaleX);
NumberUtils.TryParseSingle(bits[1], out ScaleY);
NumberUtils.TryParseSingle(bits[2], out ScaleZ);
}
string rotations = Server.rotations.Get(name);

View File

@ -53,10 +53,15 @@ namespace MCGalaxy.Tasks {
string[] args = lines[i].SplitSpaces();
if (args.Length < 9) continue;
int min = int.Parse(args[4]), hour = int.Parse(args[5]);
int day = int.Parse(args[6]), month = int.Parse(args[7]), year = int.Parse(args[8]);
int periodH = int.Parse(args[3]), periodM = 0;
if (args.Length > 10) periodM = int.Parse(args[10]);
int min = NumberUtils.ParseInt32(args[4]);
int hour = NumberUtils.ParseInt32(args[5]);
int day = NumberUtils.ParseInt32(args[6]);
int month = NumberUtils.ParseInt32(args[7]);
int year = NumberUtils.ParseInt32(args[8]);
int periodH = NumberUtils.ParseInt32(args[3]);
int periodM = 0;
if (args.Length > 10) periodM = NumberUtils.ParseInt32(args[10]);
DateTime assigned = new DateTime(year, month, day, hour, min, 0);
DateTime expiry = assigned.AddHours(periodH).AddMinutes(periodM);

View File

@ -51,7 +51,7 @@ namespace MCGalaxy
} else if (modifier.CaselessEq("all")) {
OutputItems(p, items, 0, items.Count, formatter, printer);
p.Message("Showing {0} 1-{1} (out of {1})", type, items.Count);
} else if (!int.TryParse(modifier, out page)) {
} else if (!NumberUtils.TryParseInt32(modifier, out page)) {
p.Message("Input must be either \"all\" or an integer.");
} else {
OutputPage(p, items, formatter, printer, cmd, type, page, perPage);

View File

@ -0,0 +1,65 @@
/*
Copyright 2015 MCGalaxy
Dual-licensed under the Educational Community License, Version 2.0 and
the GNU General Public License, Version 3 (the "Licenses"); you may
not use this file except in compliance with the Licenses. You may
obtain a copy of the Licenses at
https://opensource.org/license/ecl-2-0/
https://www.gnu.org/licenses/gpl-3.0.html
Unless required by applicable law or agreed to in writing,
software distributed under the Licenses are distributed on an "AS IS"
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the Licenses for the specific language governing
permissions and limitations under the Licenses.
*/
using System;
using System.Globalization;
namespace MCGalaxy
{
public static class NumberUtils
{
const NumberStyles DECIMAL_STYLE = NumberStyles.Integer | NumberStyles.AllowDecimalPoint;
const NumberStyles INTEGER_STYLE = NumberStyles.Integer;
// Not all languages use . as their decimal point separator
public static bool TryParseSingle(string s, out float result) {
if (s != null && s.IndexOf(',') >= 0) s = s.Replace(',', '.');
result = 0; float temp;
if (!Single.TryParse(s, DECIMAL_STYLE, NumberFormatInfo.InvariantInfo, out temp)) return false;
if (Single.IsInfinity(temp) || Single.IsNaN(temp)) return false;
result = temp;
return true;
}
public static bool TryParseDouble(string s, out double result) {
if (s != null && s.IndexOf(',') >= 0) s = s.Replace(',', '.');
result = 0; double temp;
if (!Double.TryParse(s, DECIMAL_STYLE, NumberFormatInfo.InvariantInfo, out temp)) return false;
if (Double.IsInfinity(temp) || Double.IsNaN(temp)) return false;
result = temp;
return true;
}
// in JSON we must use . instead of ,
public static string StringifyDouble(double value) {
return value.ToString(CultureInfo.InvariantCulture);
}
// Some languages don't have - as the negative sign symbol
public static bool TryParseInt32(string s, out int result) {
return int.TryParse(s, INTEGER_STYLE, NumberFormatInfo.InvariantInfo, out result);
}
public static int ParseInt32(string s) {
return int.Parse(s, INTEGER_STYLE, NumberFormatInfo.InvariantInfo);
}
}
}

View File

@ -68,7 +68,8 @@ namespace MCGalaxy.UI
Logger.Log(LogType.CommandUsage, "(console): /{0} can only be used in-game.", cmd.name); return;
}
Thread thread = new Thread(
Thread thread;
Server.StartThread(out thread, "ConsoleCMD_" + name,
() => {
try {
cmd.Use(Player.Console, args);
@ -82,9 +83,6 @@ namespace MCGalaxy.UI
Logger.Log(LogType.CommandUsage, "(console): FAILED COMMAND");
}
});
thread.Name = "ConsoleCMD_" + name;
thread.IsBackground = true;
thread.Start();
}
public static string Format(string message) {

View File

@ -21,9 +21,10 @@ using System.Globalization;
using System.IO;
using System.Text;
namespace MCGalaxy {
public static class Utils {
namespace MCGalaxy
{
public static class Utils
{
public static string Hex(byte r, byte g, byte b) {
return "#" + r.ToString("X2") + g.ToString("X2") + b.ToString("X2");
}
@ -64,35 +65,6 @@ namespace MCGalaxy {
/// <summary> Divides by 16, rounding up if there is a remainder. </summary>
public static int CeilDiv16(int x) { return (x + 15) / 16; }
const NumberStyles style = NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite
| NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint;
// Not all languages use . as their decimal point separator
public static bool TryParseSingle(string s, out float result) {
if (s != null && s.IndexOf(',') >= 0) s = s.Replace(',', '.');
result = 0; float temp;
if (!Single.TryParse(s, style, NumberFormatInfo.InvariantInfo, out temp)) return false;
if (Single.IsInfinity(temp) || Single.IsNaN(temp)) return false;
result = temp;
return true;
}
public static bool TryParseDouble(string s, out double result) {
if (s != null && s.IndexOf(',') >= 0) s = s.Replace(',', '.');
result = 0; double temp;
if (!Double.TryParse(s, style, NumberFormatInfo.InvariantInfo, out temp)) return false;
if (Double.IsInfinity(temp) || Double.IsNaN(temp)) return false;
result = temp;
return true;
}
// in JSON we must use . instead of ,
public static string StringifyDouble(double value) {
return value.ToString(CultureInfo.InvariantCulture);
}
public static List<string> ReadAllLinesList(string path) {