{"id":732,"date":"2014-03-19T15:41:45","date_gmt":"2014-03-19T15:41:45","guid":{"rendered":"http:\/\/gswce.net\/?p=732"},"modified":"2014-03-19T15:43:11","modified_gmt":"2014-03-19T15:43:11","slug":"printf-in-c","status":"publish","type":"post","link":"https:\/\/gswce.net\/?p=732","title":{"rendered":"printf in C#"},"content":{"rendered":"<p>I&#8217;ve been wondering about making DANSE open-source for a while now (after all if anyone else wants to start using it seriously they will need the source code), but up until recently (well, yesterday) I&#8217;ve had a problem: DANSE uses a version of printf written in C# by Richard Prinz which is only available under the Code Project Open License, and this does not allow me to release the code under any other license.  I can&#8217;t use the CPOL since there is other code also in DANSE released under the GPL.<\/p>\n<p>I can&#8217;t use the built-in printf since I want to target Silverlight as well, and I can&#8217;t just use the C# print routines since the students taking the module where this software is used haven&#8217;t learned C#, all they know is C.<\/p>\n<p>So I figured&#8230; how hard can it be to write my own version of printf?  Perhaps not implementing all the features, but at least being good enough for my purposes here?<\/p>\n<p>Turns out it&#8217;s not that hard, only took a couple of hours.  Not very well tested yet, but if anyone else would like to give the following a spin and let me know how they get on, I&#8217;d be very interested.<\/p>\n<p><code><br \/>\n        internal enum eTStype { _int, _float, _exponential, _string, _char, _pointer, _hex, _octal, _gtype, _error }<br \/>\n        internal class TS<br \/>\n        {<br \/>\n            internal int start; internal int end; internal int width; internal int precision; internal eTStype type;<br \/>\n            internal TS(int A, int B, int C, int D, eTStype what)<br \/>\n            { start = A; end = B; width = C; precision = D; type = what; }<br \/>\n        }<\/p>\n<p>        internal static string sprintf(string format, params Object[] parameters)<br \/>\n        {<br \/>\n            \/\/ Starts from a copy of the string format, then replaces the type specifiers.<br \/>\n            string result = String.Copy(format);<\/p>\n<p>            \/\/ Keep a list of the type-specifiers as I go through...<br \/>\n            List<TS> myTS = new List<TS>();<\/p>\n<p>            \/\/ The first type specifier will start with a % which is not preceeded by<br \/>\n            \/\/ an escape character '\\', include an option precision field (\".X\")<br \/>\n            \/\/ and then a length and specifier, both of which are ignored to be<br \/>\n            \/\/ friendly.  The specifier is assumed to end at the first occurrance<br \/>\n            \/\/ of any one of the following specifiers: i, d, u, o, x, X, f, F, e, E,<br \/>\n            \/\/ g, G, c, s, or p.  (C# knows what these variables are.)<\/p>\n<p>            \/\/ Go through the format string, counting how many type specifiers there<br \/>\n            \/\/ are, and making a note of what they are:<br \/>\n            for (int loop = 0; loop < format.Length; loop++)\n            {\n                if (format[loop] == '%' &#038;&#038; (loop == 0 || format[loop - 1] != '\\\\'))\n                {\n                    \/\/ Found the start of a format string.\n                    int startTS = loop;\n                    int precision = 0;\n                    int width = 0;\n                    Boolean precisionValid = false;\n                    eTStype what = eTStype._error;\n\n                    \/\/ Now I need to find the end of it and work out what it is:\n                    while (loop < format.Length &#038;&#038; what == eTStype._error)\n                    {\n                        char c = format[loop];\n                        if (c == '.') precisionValid = true;\n                        if (c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5'\n                            || c == '6' || c == '7' || c == '8' || c == '9')\n                        {\n                            string TryThis = format[loop].ToString();\n                            int whatIsThis = 0;\n                            int.TryParse(TryThis, out whatIsThis);\n                            if (precisionValid == true) precision = precision * 10 + whatIsThis;\n                            else width = width * 10 + whatIsThis;\n                        }\n                        if (c == 'i' || c == 'd' || c == 'u') what = eTStype._int;\n                        else if (c == 'o') what = eTStype._octal;\n                        else if (c == 'x' || c == 'X') what = eTStype._hex;\n                        else if (c == 'f' || c == 'F') what = eTStype._float;\n                        else if (c == 'e' || c == 'E') what = eTStype._exponential;\n                        else if (c == 'g' || c == 'G') what = eTStype._gtype;\n                        else if (c == 'c') what = eTStype._char;\n                        else if (c == 's') what = eTStype._string;\n                        else if (c == 'p') what = eTStype._pointer;\n                        loop++;\n                    }\n\n                    if (what != eTStype._error)\n                    {\n                        \/\/ Must have found the end of a format string, so add this to the list:\n                        if (precisionValid == false) precision = -1;\n                        TS ts = new TS(startTS, loop, width, precision, what);\n                        myTS.Add(ts);\n                    }\n                }\n            }\n\n            \/\/ Right - now just go through and make a new string from the \n            \/\/ non-type specifier parts of the format string, and the \n            \/\/ replacements for the type-specifiers from the parameters list:\n            StringBuilder sb = new StringBuilder(format.Length + 20 * myTS.Count);\n            int lastFinishes = 0;\n            for (int loop = 0; loop < myTS.Count; loop++)\n            {\n                int includeUpToHere = myTS[loop].start;\n                sb.Append(format.Substring(lastFinishes, includeUpToHere - lastFinishes));\n                if (parameters.Length > loop) sb.Append(makeString(myTS[loop], parameters[loop]));<br \/>\n                else sb.Append(\" ERROR - Insufficient Parameters \");<br \/>\n                lastFinishes = myTS[loop].end;<br \/>\n            }<br \/>\n            \/\/ Then add on the bit after all type specifiers:<br \/>\n            sb.Append(format.Substring(lastFinishes, format.Length - lastFinishes));<\/p>\n<p>            return sb.ToString();<br \/>\n        }<br \/>\n        static private string makeString(TS ts, Object parameter)<br \/>\n        {<br \/>\n            if (ts.type == eTStype._error) return \"*Error: Unrecognised type specifier*\";<\/p>\n<p>            \/\/ For pointers, characters and strings all that's required is:<br \/>\n            string q = parameter.ToString();<\/p>\n<p>            \/\/ Then if this is an integer, there are some special cases to include the<br \/>\n            \/\/ precision, and deal with hex and octal numbers:<br \/>\n            if (ts.type == eTStype._int)<br \/>\n            {<br \/>\n                long d;<br \/>\n                if (long.TryParse(q, out d))<br \/>\n                {<br \/>\n                    int prec = ts.precision;<br \/>\n                    if (ts.type == eTStype._hex)<br \/>\n                    {<br \/>\n                        q = d.ToString(\"X\" + ((prec < 0) ? \"\" : ts.precision.ToString()));\n                    }\n                    else if (ts.type == eTStype._octal)\n                    {\n                        \/\/ Precision not implemented for octal yet.  Does anyone care?\n                        string qq = Convert.ToString(d, 8);\n                    }\n                    else\n                    {\n                        q = d.ToString(\"D\" + ((prec < 0) ? \"\" : ts.precision.ToString()));\n                    }\n                }\n                else\n                {\n                    q = \"*Error: Unrecognised type specifier*\";\n                }\n            }\n\n            if (ts.type == eTStype._exponential || ts.type == eTStype._float || ts.type == eTStype._gtype)\n            {\n                char s = ts.type == eTStype._exponential ? 'e' : ts.type == eTStype._float ? 'f' : 'g';\n                double d; int prec = ts.precision;\n                if (double.TryParse(q, out d)) q = d.ToString(s + ((prec < 0) ? \"\" : ts.precision.ToString()));\n                else q = \"*Error: Unrecognised type specifier*\";\n            }\n\n            \/\/ Then pad (or truncate) to fit the width required:\n            if (ts.width > 0)<br \/>\n            {<br \/>\n                while (q.Length < ts.width) q = \" \" + q;\n                if (q.Length > ts.width) return q.Substring(0, ts.width);<br \/>\n                return q;<br \/>\n            }<br \/>\n            return q;<br \/>\n        }<br \/>\n    }<br \/>\n<\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve been wondering about making DANSE open-source for a while now (after all if anyone else wants to start using it seriously they will need the source code), but up until recently (well, yesterday) I&#8217;ve had a problem: DANSE uses &hellip; <a href=\"https:\/\/gswce.net\/?p=732\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/gswce.net\/index.php?rest_route=\/wp\/v2\/posts\/732"}],"collection":[{"href":"https:\/\/gswce.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gswce.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gswce.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gswce.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=732"}],"version-history":[{"count":2,"href":"https:\/\/gswce.net\/index.php?rest_route=\/wp\/v2\/posts\/732\/revisions"}],"predecessor-version":[{"id":734,"href":"https:\/\/gswce.net\/index.php?rest_route=\/wp\/v2\/posts\/732\/revisions\/734"}],"wp:attachment":[{"href":"https:\/\/gswce.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=732"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gswce.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=732"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gswce.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=732"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}