Добавил команду с выдачей погоды

This commit is contained in:
ftc
2021-04-08 22:35:23 +03:00
parent 41269da76c
commit 91b4b069f4
4 changed files with 917 additions and 10 deletions
+72 -10
View File
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Reflection;
@@ -18,11 +20,13 @@ namespace ZaoBot
static JsonDocument config;
static HttpClient http = new HttpClient();
const string CMD_WEATHER = "Погода";
const string CMD_LEFT_GATE = "Открой левые ворота";
const string CMD_RIGHT_GATE = "Открой правые ворота";
static void Main(string[] args)
{
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
config = LoadConfig();
var token = config.RootElement.GetProperty("token").GetString();
botClient = new TelegramBotClient(token);
@@ -48,18 +52,32 @@ namespace ZaoBot
}
Console.WriteLine($"Received a text message in chat {e.Message.Chat.Id}.");
var response = "";
if (e.Message.Text == CMD_LEFT_GATE)
{
await OpenLeftGate();
response = await OpenLeftGate();
}
if (e.Message.Text == CMD_RIGHT_GATE)
else if (e.Message.Text == CMD_RIGHT_GATE)
{
await OpenRightGate();
response = await OpenRightGate();
}
else if (e.Message.Text == CMD_WEATHER)
{
response = await ShowWeather();
}
else if (e.Message.Text == "")
{
response = "Превед!";
}
else response = "Я не понял :-(";
if (e.Message.Text != "") response += "\nЧто дальше?";
else response = "\nЧего изволите?";
var replyKeyboardMarkup = new ReplyKeyboardMarkup(
new KeyboardButton[][]
{
new KeyboardButton[] { CMD_WEATHER },
new KeyboardButton[] { CMD_LEFT_GATE, CMD_RIGHT_GATE },
},
resizeKeyboard: true
@@ -67,31 +85,75 @@ namespace ZaoBot
await botClient.SendTextMessageAsync(
chatId: e.Message.Chat,
text: "Чего изволите?",
text: response,
replyMarkup: replyKeyboardMarkup
);
}
}
private static async Task OpenRightGate()
private static async Task<string> ShowWeather()
{
try
{
string response = "";
foreach (var element in config.RootElement.GetProperty("items_weather").EnumerateObject())
{
var url = config.RootElement.GetProperty("openhab_url").GetString();
url += element.Name;
var result = await http.GetAsync(url);
var content = await result.Content.ReadAsStringAsync();
response += element.Value + ": " + ParseContent(content) + "\n";
}
return response;
}
catch (Exception e)
{
return "Fail: " + e.Message;
}
}
private static string ParseContent(string content)
{
var doc = JsonDocument.Parse(content);
float value;
if (!float.TryParse(doc.RootElement.GetProperty("state").GetString(), out value))
{
return "null";
}
try
{
var stateDescription = doc.RootElement.GetProperty("stateDescription");
var pattern = stateDescription.GetProperty("pattern");
return printf.Printf.sprintf(pattern.GetString(), value);
}
catch (KeyNotFoundException)
{
return value.ToString();
}
}
private static async Task<string> OpenRightGate()
{
var url = config.RootElement.GetProperty("openhab_url").GetString();
url += config.RootElement.GetProperty("item_right_gate").GetString();
await Post(url, "ON");
return await Post(url, "ON");
}
private static async Task Post(string url, string v)
private static async Task<string> Post(string url, string v)
{
var content = new ByteArrayContent(Encoding.UTF8.GetBytes(v));
content.Headers.Add("Content-Type", "text/plain");
await http.PostAsync(url, content);
var result = await http.PostAsync(url, content);
if (result.IsSuccessStatusCode) return "OK";
else return "Fail. " + result.ReasonPhrase;
}
private static async Task OpenLeftGate()
private static async Task<string> OpenLeftGate()
{
var url = config.RootElement.GetProperty("openhab_url").GetString();
url += config.RootElement.GetProperty("item_left_gate").GetString();
await Post(url, "ON");
return await Post(url, "ON");
}
private static bool VerifyUser(long id, string username)
+535
View File
@@ -0,0 +1,535 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace printf {
/// <summary>
/// Applies string formatting to objects using a "printf" format string.
/// Can be extended to use custom formats.
/// </summary>
public class FormatObject {
///<summary>
/// The default precision for floating point numbers
/// </summary>
public const int DefaultPrecision = 6;
/// <summary>
/// Represents a formatting part of the format string (i.e. %1.3d)
/// All fields are interpreted as specified in the C language specification
/// </summary>
public class FormatStringPart {
/// <summary>
/// Minimum width
/// </summary>
public int width = 0;
/// <summary>
/// Precision
/// </summary>
public int? precision;
/// <summary>
/// Length (h or l)
/// </summary>
public char length;
/// <summary>
/// Specifier (s for strings, d for integers etc.)
/// </summary>
public char specifier;
/// <summary>
/// Left aligns the result instead of the default right align
/// </summary>
public bool LeftAlign = false;
/// <summary>
/// Displays + cahracter for positive numbers (by default it is omitted)
/// </summary>
public bool ForcePlus = false;
/// <summary>
/// Displays ' ' (space) character for positive numbers (by default it is omitted)
/// </summary>
public bool BlankIfPlus = false;
/// <summary>
/// Has different meanings for format specifiers
/// </summary>
public bool HashMark = false;
/// <summary>
/// If LeftAlign = false, pads with the '0' character instead of ' '.
/// If LeftAlign = true, this flag is ignored.
/// </summary>
public bool PadWithZero = false;
}
//List of actual arguments
List<object> args = new List<object>();
//Number of required arguments
int argCount = 0;
//Format string parts
FormatStringPart[] parts;
string[] staticParts;
string finalString;
enum ParseMode {
Static, Format
}
enum FormatStep {
Flags, Width, PrecisionStart, Precision, Length, Specifier
}
/// <summary>
/// Creates a new formatter with the default formatters (used by the printf functions).
/// </summary>
/// <param name="format"></param>
public FormatObject(string format): this(format, true) {}
/// <summary>
/// Creates a new formatter with or without the default formatters (used by the printf functions).
/// </summary>
/// <param name="format">The format string</param>
/// <param name="useDefaultFormatters">Load the default formatters or not</param>
public FormatObject(string format, bool useDefaultFormatters) {
//State machine to parse the format string
//Surprisingly, the regexp version is not really shorter:
//Parsing the string is easier, but extracting the results is longer
List<FormatStringPart> parts = new List<FormatStringPart>();
List<string> staticParts = new List<string>();
StringBuilder staticPart = new StringBuilder();
//Static mode: not in a % block, Format mode: in a % block
ParseMode mode = ParseMode.Static;
FormatStringPart part = null;
FormatStep step = FormatStep.Flags;
StringBuilder tempBuffer = null;
for (int i = 0; i < format.Length; ++i) {
char c = format[i];
if (mode == ParseMode.Static) {
//If
if (c == '%') {
mode = ParseMode.Format;
step = FormatStep.Flags;
}
else {
staticPart.Append(c);
}
}
else {
//For the terminology (flags, specifiers etc.) look up any printf documentation.
switch (step) {
case FormatStep.Flags:
if (c == '%') {
staticPart.Append('%');
mode = ParseMode.Static;
continue;
}
if (part == null) part = new FormatStringPart();
if (c == '-') {
part.LeftAlign = true;
}
else if (c == '+') {
part.ForcePlus = true;
}
else if (c == ' ') {
part.BlankIfPlus = true;
}
else if (c == '#') {
part.HashMark = true;
}
else if (c == '0') {
part.PadWithZero = true;
}
else {
step = FormatStep.Width;
goto case FormatStep.Width;
}
break;
case FormatStep.Width:
if (tempBuffer != null) {
if (c >= '0' && c <= '9') {
tempBuffer.Append(c);
}
else {
part.width = int.Parse(tempBuffer.ToString());
tempBuffer = null;
step = FormatStep.PrecisionStart;
goto case FormatStep.PrecisionStart;
}
}
else if (c == '*') {
part.width = -1;
argCount++;
}
else if (c >= '0' && c <= '9') {
tempBuffer = new StringBuilder();
tempBuffer.Append(c);
}
else {
step = FormatStep.PrecisionStart;
goto case FormatStep.PrecisionStart;
}
break;
case FormatStep.PrecisionStart:
if (c != '.') {
step = FormatStep.Length;
goto case FormatStep.Length;
}
else {
step = FormatStep.Precision;
}
break;
case FormatStep.Precision:
if (tempBuffer != null) {
if (c >= '0' && c <= '9') {
tempBuffer.Append(c);
}
else {
part.precision = int.Parse(tempBuffer.ToString());
tempBuffer = null;
step = FormatStep.Length;
goto case FormatStep.Length;
}
}
else if (c == '*') {
part.precision = -1;
argCount++;
}
else if (c >= '0' && c <= '9') {
tempBuffer = new StringBuilder();
tempBuffer.Append(c);
}
else {
part.precision = 0;
step = FormatStep.Length;
goto case FormatStep.Length;
}
break;
case FormatStep.Length:
if ("hlL".IndexOf(c) != -1) {
part.length = c;
step = FormatStep.Specifier;
}
else {
step = FormatStep.Specifier;
goto case FormatStep.Specifier;
}
break;
case FormatStep.Specifier:
argCount++;
part.specifier = c;
staticParts.Add(staticPart.ToString());
staticPart = new StringBuilder();
parts.Add(part);
part = null;
mode = ParseMode.Static;
break;
}
}
}
if (mode == ParseMode.Format) {
throw new ArgumentException("Invalid format string", "format");
}
staticParts.Add(staticPart.ToString());
this.parts = parts.ToArray();
this.staticParts = staticParts.ToArray();
if (useDefaultFormatters) LoadDefaultFormatters();
}
/// <summary>
/// Set the objects to format.
/// If they are already set, the function will overwrite them.
/// This is useful if you want to use the same format string to
/// format many objects.
/// </summary>
/// <param name="objs">The list of objects</param>
public void SetArgs(params object[] objs) {
for (int i = 0; i < objs.Length; ++i) {
args.Add(objs[i]);
}
this.finalString = null;
}
/// <summary>
/// Formats the object according to the FormatStringPart argument.
/// If you create a custom formatter, you should ignore the width property,
/// as padding is done independently for all format types.
/// </summary>
/// <param name="part">It contains the flags, specifier etc.</param>
/// <param name="arg">The object to format</param>
public delegate FormatResult Formatter(FormatStringPart part, object arg);
Dictionary<char, Formatter> formatters = new Dictionary<char, Formatter>();
/// <summary>
/// Adds a custom formatter.
/// </summary>
/// <param name="specifier">The character in the format string (e.g. 's' for %s)</param>
/// <param name="formatter">The formatter function</param>
public void AddFormatter(char specifier, Formatter formatter) {
formatters.Add(specifier, formatter);
}
private void DoFormat() {
try {
if (argCount > args.Count) {
throw new InvalidOperationException(string.Format(
"Expected argument count: {0}, provided: {1}", argCount, args.Count));
}
StringBuilder final = new StringBuilder();
for (int i = 0, j = 0; i < parts.Length; ++i, ++j) {
final.Append(staticParts[i]);
var f = parts[j];
if (f.width == -1) {
f.width = (int)args[j];
j++;
}
if (f.precision == -1) {
f.width = (int)args[j];
j++;
}
FormatResult afterSpecifiers = formatters[f.specifier].Invoke(f, args[j]);
int pad = f.width - afterSpecifiers.Result.Length - afterSpecifiers.Sign.Length;
if (pad > 0 && !f.LeftAlign) {
if (f.PadWithZero) {
final.Append(afterSpecifiers.Sign);
final.Append(new string('0', pad));
}
else {
final.Append(new string(' ', pad));
final.Append(afterSpecifiers.Sign);
}
}
else {
final.Append(afterSpecifiers.Sign);
}
final.Append(afterSpecifiers.Result);
if (pad > 0 && f.LeftAlign) {
final.Append(new string(' ', pad));
}
}
final.Append(staticParts[staticParts.Length-1]);
finalString = final.ToString();
}
catch (KeyNotFoundException ex) {
throw new InvalidOperationException("Invalid specifier in format string", ex);
}
}
/// <summary>
/// Returns the formatted string.
/// </summary>
/// <returns>The formatted string</returns>
public override string ToString() {
if (this.finalString == null) DoFormat();
return finalString;
}
/// <summary>
/// The results of the Formatter type functions.
/// </summary>
public struct FormatResult {
/// <summary>
/// The resulting strings sign, if any.
/// </summary>
public string Sign {get; set;}
/// <summary>
/// The resulting string, without the sign if any.
/// </summary>
public string Result {get; set;}
/// <summary>
/// Converts to string into a FormatResult object.
/// The string is assigned to the Result property of the created FormatString,
/// the Sign property will be "" (empty string).
/// </summary>
/// <param name="s">The string to convert</param>
/// <returns>A FormatResult object</returns>
public static implicit operator FormatResult(string s) {
return new FormatResult {
Sign = "", Result = s
};
}
}
private string IntPrecision(string str, FormatStringPart part) {
if (str.Equals("0") && part.precision == 0) return "";
else if (str.Length < part.precision) {
return new string('0', (part.precision ?? 1) - str.Length) + str;
}
else {
return str;
}
}
private void LoadDefaultFormatters() {
var format = new System.Globalization.NumberFormatInfo();
format.NumberDecimalSeparator = ".";
Formatter charFormatter = (part, arg) => {
char c = (char)arg;
return c.ToString();
};
AddFormatter('c', charFormatter);
Formatter intFormatter = (part, arg) => {
long i = Convert.ToInt64(arg);
string sign = "";
if (i >= 0) {
if (part.ForcePlus) sign = "+";
else if (part.BlankIfPlus) sign = " ";
}
else {
i = -i;
sign = "-";
}
return new FormatResult { Result = IntPrecision(i.ToString(), part), Sign = sign };
};
AddFormatter('i', intFormatter);
AddFormatter('d', intFormatter);
Formatter uintFormatter = (part, arg) => {
ulong u = Convert.ToUInt64(arg);
string sign = "";
if (u >= 0) {
if (part.ForcePlus) sign = "+";
else if (part.BlankIfPlus) sign = " ";
}
return new FormatResult { Result = IntPrecision(u.ToString(), part) , Sign = sign };
};
AddFormatter('u', uintFormatter);
Formatter sciFormatter = (part, arg) => {
double d = Convert.ToDouble(arg);
string sign = "";
if (d >= 0 || double.IsNaN(d)) {
if (part.ForcePlus) sign = "+";
else if (part.BlankIfPlus) sign = " ";
}
else {
d = -d;
sign = "-";
}
string retStr = d.ToString(string.Concat("0.",
new string('0', part.precision ?? DefaultPrecision),
part.specifier.ToString(),
"+000"),
format);
//# flag: place decimal point even if not needed
if (part.HashMark && !retStr.Contains(format.NumberDecimalSeparator)) {
retStr = string.Join(format.NumberDecimalSeparator + part.specifier,
retStr.Split(part.specifier));
}
return new FormatResult {
Result = retStr,
Sign = sign
};
};
AddFormatter('e', sciFormatter);
AddFormatter('E', sciFormatter);
Formatter floatFormatter = (part, arg) => {
double d = Convert.ToDouble(arg);
string sign = "";
if (d >= 0) {
if (part.ForcePlus) sign = "+";
else if (part.BlankIfPlus) sign = " ";
}
else {
d = -d;
sign = "-";
}
format.NumberDecimalDigits = part.precision ?? DefaultPrecision;
string retStr = d.ToString("f", format);
//# flag: place decimal point even if not needed
if (part.HashMark && !retStr.Contains(format.NumberDecimalSeparator)) {
retStr += format.NumberDecimalSeparator;
}
return new FormatResult { Result = retStr, Sign = sign };
};
AddFormatter('f', floatFormatter);
Formatter gFormatter = (part, arg) => {
double d = Convert.ToDouble(arg);
string sign = "";
if (d >= 0) {
if (part.ForcePlus) sign = "+";
else if (part.BlankIfPlus) sign = " ";
}
else {
d = -d;
sign = "-";
}
format.NumberDecimalDigits = part.precision ?? DefaultPrecision;
/*string formatString = part.specifier.ToString() + part.precision.ToString() +
(part.HashMark ? ":0." + new string('#', part.precision ?? DefaultPrecision) : "");
string retStr = string.Format(format, formatString, d);*/
//Could not make it work
string retStr = d.ToString(part.specifier.ToString() + part.precision.ToString(), format);
return new FormatResult { Result = retStr, Sign = sign };
};
AddFormatter('g', gFormatter);
AddFormatter('G', gFormatter);
Formatter octFormatter = (part, arg) => {
long l = Convert.ToInt64(arg);
string retStr = IntPrecision(Convert.ToString(l, 8), part);
//# flag: put a 0 before tha number
if (part.HashMark) {
return "0" + retStr;
}
else {
return retStr;
}
};
AddFormatter('o', octFormatter);
Formatter hexFormatter = (part, arg) => {
string retStr;
if (part.length == 'h') {
short s = Convert.ToInt16(arg);
retStr = string.Format(
part.specifier == 'x' ? "{0:x}" : "{0:X}",
s);
}
else if (part.length == 'l') {
long l = Convert.ToInt64(arg);
retStr = string.Format(
part.specifier == 'x' ? "{0:x}" : "{0:X}",
l);
}
else {
int i = Convert.ToInt32(arg);
retStr = string.Format(
part.specifier == 'x' ? "{0:x}" : "{0:X}",
i);
}
//# flag: put a 0x before tha number
return new FormatResult {
Result = IntPrecision(retStr, part),
Sign = part.HashMark ? "0" + part.specifier : ""
};
};
AddFormatter('x', hexFormatter);
AddFormatter('X', hexFormatter);
Formatter strFormatter = (part, arg) => {
if (part.precision != null) {
return arg.ToString().Remove(part.precision.Value);
}
else {
return arg.ToString();
}
};
AddFormatter('s', strFormatter);
Formatter ptrFormatter = (part, arg) => {
return arg.GetHashCode().ToString();
};
AddFormatter('p', ptrFormatter);
AddFormatter('n', (part, arg) => {
throw new NotSupportedException("Getting the number of characters is not supported");
});
}
}
}
+241
View File
@@ -0,0 +1,241 @@
#if TEST
using System;
using NUnit.Framework;
namespace printf {
[TestFixture]
public class FormatTest {
string decSep = System.Globalization.NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
[Test]
public void TestTypes() {
//Test the basic data types
Assert.AreEqual("b",
Printf.sprintf("%c", 'b'));
Assert.AreEqual("121",
Printf.sprintf("%d", 121));
Assert.AreEqual("-34",
Printf.sprintf("%i", -34));
Assert.AreEqual("3.123400e+002",
Printf.sprintf("%e", 3.1234e02));
Assert.AreEqual("3.123400E+002",
Printf.sprintf("%E", 3.1234e02));
Assert.AreEqual("3.123000",
Printf.sprintf("%f", 3.123));
Assert.AreEqual("610",
Printf.sprintf("%o", Convert.ToInt32("0610", 8)));
Assert.AreEqual("asdfjkl",
Printf.sprintf("%s", "asdfjkl"));
Assert.AreEqual("4567",
Printf.sprintf("%u", 4567));
Assert.AreEqual("deadf00d",
Printf.sprintf("%x", unchecked((int)0xdeadf00d)));
Assert.AreEqual("DEADF00D",
Printf.sprintf("%X", unchecked((int)0xdeadf00d)));
Assert.IsNotEmpty(Printf.sprintf("%p", new object()));
Assert.Throws<NotSupportedException>(delegate() {
Printf.sprintf("%n", new IntPtr());
});
//Invalid format string
Assert.Throws<ArgumentException>(delegate() {
Printf.sprintf("%z", new IntPtr());
});
Assert.AreEqual("%",
Printf.sprintf("%%"));
}
[Test]
public void TestStaticText() {
//Static text
Assert.AreEqual("aaaa",
Printf.sprintf("aaaa"));
//Param at the end
Assert.AreEqual("Hello World",
Printf.sprintf("Hello %s", "World"));
//Param at the beginning
Assert.AreEqual("Hello World!",
Printf.sprintf("%s World!", "Hello"));
//Param in the middle
Assert.AreEqual("a 1 b",
Printf.sprintf("a %d b", 1));
//Multiple params
Assert.AreEqual("Hello World!",
Printf.sprintf("%s %s!", "Hello", "World"));
}
[Test]
public void TestArgumentCount() {
//More arguments should not cause errors
Assert.DoesNotThrow(delegate() {
Printf.sprintf("%d %d", 1, 2, 3);
});
//Less arguments should raise error
Assert.Throws<ArgumentException>(delegate() {
Printf.sprintf("%d %d", 1);
});
//Also check for the extra arguments required for *
Assert.Throws<ArgumentException>(delegate() {
Printf.sprintf("%*d", 1);
});
Assert.Throws<ArgumentException>(delegate() {
Printf.sprintf("%.*d", 1);
});
Assert.Throws<ArgumentException>(delegate() {
Printf.sprintf("%*.*d", 1, 2);
});
Assert.DoesNotThrow(delegate() {
Printf.sprintf("%*.*d", 1, 2, 3);
});
}
[Test]
public void TestPadding() {
//Simple padding
Assert.AreEqual(" 33",
Printf.sprintf("%5d", 33));
//More padding than required
Assert.AreEqual("123456",
Printf.sprintf("%4d", 123456));
//Left-align
Assert.AreEqual("888 ",
Printf.sprintf("%-5d", 888));
//Pad with zeros
Assert.AreEqual("0043, 00zz",
Printf.sprintf("%04d, %04s", 43, "zz"));
//Left align always pads with spaces
Assert.AreEqual("123 ",
Printf.sprintf("%-05d", 123));
//Width specified on the argument list
Assert.AreEqual(" 11",
Printf.sprintf("%*d", 6, 11));
//Sign with padding
Assert.AreEqual(" -1 -0001 0001",
Printf.sprintf("%5d %05d % 05d", -1, -1, 1));
}
[Test]
public void TestSign() {
//Sign of number
Assert.AreEqual("-5",
Printf.sprintf("%d", -5));
//Positive signs
Assert.AreEqual("3 +4",
Printf.sprintf("%d %+d", 3, 4));
//Sign is not printed for o x and X
Assert.AreEqual("610 ffffffff FFFFFFFF",
Printf.sprintf("%+o %+x %+X", Convert.ToInt32("0610", 8), -1, -1));
//Insert space if there is no sign
Assert.AreEqual("-11 22",
Printf.sprintf("% d % d", -11, 22));
}
[Test]
public void TestPrecision() {
//INTEGERS: minimum number of digits
Assert.AreEqual("123",
Printf.sprintf("%.2d", 123));
Assert.AreEqual("ff",
Printf.sprintf("%.1x", 0xff));
//Padding with 0
Assert.AreEqual("0044",
Printf.sprintf("%.4d", 44));
Assert.AreEqual("000FF",
Printf.sprintf("%.5X", 0xff));
Assert.AreEqual("003",
Printf.sprintf("%.3u", 3));
Assert.AreEqual("0010",
Printf.sprintf("%.4o", 8));
//Sign is not included:
Assert.AreEqual("-01",
Printf.sprintf("%.2d", -1));
//Mixing:
Assert.AreEqual(" 001",
Printf.sprintf("%5.3d", 1));
//Zero:
Assert.AreEqual("00",
Printf.sprintf("%.2d", 0));
Assert.AreEqual("",
Printf.sprintf("%.0d", 0));
Assert.AreEqual("",
Printf.sprintf("%.d", 0));
//Floating point:
Assert.AreEqual("0.1235",
Printf.sprintf("%.4f", 0.123456));
Assert.AreEqual("0",
Printf.sprintf("%.0f", 0.123456));
//Mixing:
Assert.AreEqual(" 0.123",
Printf.sprintf("%6.3f", 0.123));
Assert.AreEqual("0.123",
Printf.sprintf("%4.3f", 0.123));
//Scientific:
Assert.AreEqual("1.2346e-001",
Printf.sprintf("%.4e", 0.123456));
Assert.AreEqual("1e-001",
Printf.sprintf("%.0e", 0.123456));
//Mixing:
Assert.AreEqual("1.235e-001",
Printf.sprintf("%4.3e", 0.123456));
Assert.AreEqual(" 1.235e-001",
Printf.sprintf("%15.3e", 0.123456));
//String:
Assert.AreEqual("asd",
Printf.sprintf("%.3s", "asdfjkl"));
Assert.AreEqual("",
Printf.sprintf("%.s", "asdfjkl"));
//Mixing:
Assert.AreEqual(" asd",
Printf.sprintf("%6.3s", "asdfjkl"));
}
[Test]
public void TestHashMark() {
//Prefix for o, h and H
Assert.AreEqual("011 0xff 0XABC",
Printf.sprintf("%#o %#x %#X", 9, 0xff, 0xabc));
//Decimal point for f, e and E
Assert.AreEqual("1. 2.e+000 3.E+000",
Printf.sprintf("%#.0f %#.0e %#.0E", 1.1, 2.1, 3.1));
Assert.AreEqual("1.1 2.1e+000 3.1E+000",
Printf.sprintf("%#.1f %#.1e %#.1E", 1.1, 2.1, 3.1));
//Mixing with 0 and padding
Assert.AreEqual("0x0ab",
Printf.sprintf("%#05x", 0xab));
}
[Test]
public void TestGSpecifier() {
Assert.AreEqual("0.0001",
Printf.sprintf("%g", 0.0001));
Assert.AreEqual("1e-05",
Printf.sprintf("%g", 0.00001));
Assert.AreEqual("1E-05",
Printf.sprintf("%G", 0.00001));
//Trailing zeros
Assert.AreEqual("0.01",
Printf.sprintf("%.3g", 0.01));
Assert.AreEqual("0.010",
Printf.sprintf("%.3f", 0.01));
//Assert.AreEqual("0.010",
// Printf.sprintf("%#.3g", 0.01));
//Precision = significant digits
Assert.AreEqual(" 10.1",
Printf.sprintf("%9.3g", 10.111111));
}
}
}
#endif
+69
View File
@@ -0,0 +1,69 @@
using System;
namespace printf {
/// <summary>
/// Contains the printf family of functions.
/// It supports the following formats: 'diufeExXoscp';
/// The following flags: '-+ #0';
/// Width and precision;
/// Length (h, l) for hexadecimal output (for other formats, integers are treated as Int64 or UInt64)
///
/// Decimal separator is not localized, it is always a '.' character.
/// %g and %G does not remove trailing zeroes.
/// NaN and infinities are not guaranteed to have a fixed representation accross platforms.
///
/// At the moment, it does NOT support %n (number of chars printed, through a prointer),
/// and the 1$..n$ position specifier.
/// </summary>
public static class Printf {
/// <summary>
/// Formats the arguments according to the format string.
/// Returns the result as a string.
/// </summary>
/// <param name="format">The format string</param>
/// <param name="args">The objects to format</param>
/// <returns>The formatted output</returns>
/// <exception cref="ArgumentException">The format string is invalid or too few arguments provided.</exception>
/// <exception cref="ArgumentNullException">Format string is null</exception>
public static string sprintf(string format, params object[] args) {
if (format == null) throw new ArgumentNullException("format");
try {
FormatObject f = new FormatObject(format);
f.SetArgs(args);
return f.ToString();
}
catch(InvalidOperationException ex) {
throw new ArgumentException("Error in format string or arguments, see inner exception for details", ex);
}
}
/// <summary>
/// Formats the arguments according to the format string,
/// and writes the formatted string to the standard output.
/// </summary>
/// <param name="format">The format string</param>
/// <param name="args">The objects to format</param>
/// <exception cref="ArgumentException">The format string is invalid or too few arguments provided.</exception>
/// <exception cref="ArgumentNullException">Format string is null</exception>
public static void printf(string format, params object[] args) {
if (format == null) throw new ArgumentNullException("format");
Console.Write(sprintf(format, args));
}
/// <summary>
/// Formats the arguments according to the format string,
/// and writes the formatted string to the specified TextWriter.
/// </summary>
/// <param name="writer">The target TextWriter</param>
/// <param name="format">The format string</param>
/// <param name="args">The objects to format</param>
/// <exception cref="ArgumentException">The format string is invalid or too few arguments provided.</exception>
/// <exception cref="ArgumentNullException">Format string or TextWriter is null</exception>
public static void fprintf(System.IO.TextWriter writer, string format, params object[] args) {
if (format == null) throw new ArgumentNullException("format");
if (writer == null) throw new ArgumentNullException("writer");
writer.Write(sprintf(format, args));
}
}
}