Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« : 09-12-2009 17:01 » |
|
Может кто-нибудь знает штатное средство в .NET, умеющее печатать строчку по формату, заданному в стиле C-шного printf?
Т.е. на входе имеется строка типа "f%04ds", как с её помощью в .NET получить серию строк вида "f0000s", "f0001s" и т.д.
Как-то не радует перспектива писать свой конвертор C-style форматных строк в .NET-style форматные вида "f{0:0000}s".
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Malaja
|
|
« Ответ #1 : 09-12-2009 17:14 » |
|
Дим, ты имеешь в виду вот это: strStr = String.Format("int = {0:D} *** float = {1:f} *** {2:S} *** {3:0}", 5, 5.5, "lala", 0.000);
на выходе: "int = 5 *** float = 5,50 *** lala *** 0" или я не так поняла? Тогда извини...
|
|
|
Записан
|
холоднокровней, Маня, Ви не на работе --------------------------------------- четкое определение сущности бытия: - А мы в прошлом или в будущем?- спросила Алиса. - Мы в жопе, - ответил кролик. - А "жопа" - это настоящее? - спросила Алиса. - А "жопа" - это у нас символ вечности.
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #2 : 09-12-2009 17:47 » |
|
А вставка блока кода на с++ возможна ?
|
|
|
Записан
|
|
|
|
Malaja
|
|
« Ответ #3 : 09-12-2009 18:00 » |
|
Леш, куда ты хочешь его вставлять?
|
|
|
Записан
|
холоднокровней, Маня, Ви не на работе --------------------------------------- четкое определение сущности бытия: - А мы в прошлом или в будущем?- спросила Алиса. - Мы в жопе, - ответил кролик. - А "жопа" - это настоящее? - спросила Алиса. - А "жопа" - это у нас символ вечности.
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #4 : 09-12-2009 18:07 » |
|
Может я что-то путаю, Ну вроде дот-нет позиционировался как кроссплатформенный и кроссязыковый ? И можно делать вставки на любом языке, так же, как в с++ можно делать вставки ассемблера
|
|
« Последнее редактирование: 10-12-2009 03:51 от Алексей1153++ »
|
Записан
|
|
|
|
Malaja
|
|
« Ответ #5 : 09-12-2009 21:48 » |
|
Леш, не знаю, ничего не могу сказать...
|
|
|
Записан
|
холоднокровней, Маня, Ви не на работе --------------------------------------- четкое определение сущности бытия: - А мы в прошлом или в будущем?- спросила Алиса. - Мы в жопе, - ответил кролик. - А "жопа" - это настоящее? - спросила Алиса. - А "жопа" - это у нас символ вечности.
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #6 : 10-12-2009 08:59 » |
|
Malaja, я имею в виду вот это как конечный результат преобразования, а на входе форматная строка с синтаксисом printf.
В общем, свой конвертор написал.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Malaja
|
|
« Ответ #7 : 10-12-2009 10:52 » |
|
понятно. тогда прости за ненужную инфу.
|
|
|
Записан
|
холоднокровней, Маня, Ви не на работе --------------------------------------- четкое определение сущности бытия: - А мы в прошлом или в будущем?- спросила Алиса. - Мы в жопе, - ответил кролик. - А "жопа" - это настоящее? - спросила Алиса. - А "жопа" - это у нас символ вечности.
|
|
|
Sla
|
|
« Ответ #8 : 10-12-2009 10:59 » |
|
ой, нескромно так Код в студию
|
|
|
Записан
|
Мы все учились понемногу... Чему-нибудь и как-нибудь.
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #9 : 10-12-2009 15:25 » |
|
На выходных. Он у меня сейчас не универсальный, а заточенный только под целые числа и под один аргумент.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #10 : 12-12-2009 13:08 » |
|
Собственно, вот. class C { private class CFormat { [Flags] private enum FormatFlags { None = 0, LeftAlign = 1, PlusSign = 2, FirstZeros = 4, Blank = 8, LastZeros = 16 }
private struct NumberParameter { public bool Exist; public int? Value; }
private delegate object NextArg(Type expectedType);
private string result; private Stack args;
public CFormat(string format, params object[] args) { this.args = new Stack(); for (int i = args.Length - 1; i >= 0; --i) { this.args.Push(args[i]); } Regex regex = new Regex(@"%(?<flag>[-+0 #]*)(?<width>\*|\d+)?(?<precision>\.\d*)?(?<size>[hlIw]|ll|I32|I64)?(?<type>[cCdiouxXeEfgGnpsS])"); this.result = regex.Replace(format, new MatchEvaluator(this.replace)).Replace("%%", "%"); }
public string Result { get { return this.result; } }
private object getNextArg(Type expectedType) { if (this.args.Count == 0) { throw new ArgumentOutOfRangeException(); } object arg = this.args.Pop(); if (arg.GetType() != expectedType) { throw new InvalidCastException(); } return arg; }
private string replace(Match match) { FormatFlags flags = this.parseFlags(match.Groups["flag"].Value); NumberParameter width = this.parseWidth(match.Groups["width"].Value, this.getNextArg); NumberParameter precision = this.parsePrecision(match.Groups["precision"].Value); string type = match.Groups["type"].Value; object value = this.getNextArg(this.detectValueType(type, match.Groups["size"].Value)); string result = string.Empty; switch (type) { case "i": case "d": Int64 intValue = Convert.ToInt64(value); result = string.Format("{0:D}", Math.Abs(intValue)); string sign = string.Empty; if (intValue >= 0) { if ((flags & FormatFlags.PlusSign) != 0) { sign = "+"; } else if ((flags & FormatFlags.Blank) != 0) { sign = " "; } } else { sign = "-"; } int fieldSize = width.Value.HasValue ? width.Value.Value - (sign + result).Length : 0; StringBuilder fieldBuilder = new StringBuilder(); char fieldChar = (flags & FormatFlags.FirstZeros) != 0 && !precision.Exist && (flags & FormatFlags.LeftAlign) == 0 ? '0' : ' '; for(int i = 0; i < fieldSize; ++i) { fieldBuilder.Append(fieldChar); } string field = fieldBuilder.ToString(); if (field.Length > 0 && field[0] == '0') { result = sign + field + result; } else if ((flags & FormatFlags.LeftAlign) != 0) { result = sign + result + field; } else { result = field + sign + result; } break; default: throw new InvalidDataException(); } return result; }
private Type detectValueType(string type, string size) { switch (type) { case "i": case "d": switch (size) { case "h": return typeof(short); case "l": return typeof(long); case "ll": case "I64": return typeof(Int64); case "I32": return typeof(Int32); default: return typeof(int); } default: return null; } }
private FormatFlags parseFlags(string flags) { FormatFlags result = FormatFlags.None; foreach (char c in flags) { switch (c) { case '-': result |= FormatFlags.LeftAlign; break; case '+': result |= FormatFlags.PlusSign; break; case ' ': result |= FormatFlags.Blank; break; case '0': result |= FormatFlags.FirstZeros; break; case '#': result |= FormatFlags.LastZeros; break; } } return result; }
private NumberParameter parseNumberParameter(string numberParameter) { NumberParameter result = new NumberParameter(); result.Exist = numberParameter != string.Empty; if(result.Exist) { result.Value = Convert.ToInt32(numberParameter); } else { result.Value = null; } return result; }
private NumberParameter parseWidth(string width, NextArg getNextArg) { if (width == "*") { NumberParameter result = new NumberParameter(); result.Exist = true; result.Value = Convert.ToInt32(getNextArg(typeof(int))); return result; } else { return this.parseNumberParameter(width); } }
private NumberParameter parsePrecision(string precision) { bool isPoint = precision.Length > 0 && precision[0] == '.'; if (isPoint) { NumberParameter result = this.parseNumberParameter(precision.Substring(1)); result.Exist = true; return result; } else { return this.parseNumberParameter(precision); } } }
public static string sprintf(string format, params object[] args) { return new CFormat(format, args).Result; }
public static void printf(string format, params object[] args) { Console.Write(C.sprintf(format, args)); }
public static void fprintf(Stream openedFile, string format, params object[] args) { new StreamWriter(openedFile).Write(C.sprintf(format, args)); } } Сделал универсальный и расширяемый анализатор, но из всех возможных значений типов терпения хватило только на целые числа. Остальное мне сейчас не надо, а нуждающиеся допишут сами. Использование типа: C.printf("'%-8ld', '%+08i' '%*d'\n", (long)12, -32, 8, 24); Вывод: '12 ', '-0000032' ' 24'
Кто баги найдёт - пишите. Ведёт оно себя строже, чем в C, в части контроля типов. P.S. Добавил звёздочку (см. ниже).
|
|
« Последнее редактирование: 13-12-2009 08:36 от Dimka »
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #11 : 12-12-2009 13:36 » |
|
Ещё есть звёздочка, я вот так сразу не нашёл, где обрабатывается. Например ...("% *d",7,5) -> " 5"
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #12 : 12-12-2009 14:45 » |
|
Алексей1153++, да, звёздочки в этой реализации нет. Нужно расширить регулярное выражение и кое-что перекомпоновать.
Пока добавлял звёздочку, баг нашёл из-за порчи дизайна - говорили же умные люди, что нефиг в функции побочные эффекты закладывать. Хорошо бы перепроектировать.
|
|
« Последнее редактирование: 12-12-2009 15:08 от Dimka »
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #13 : 13-12-2009 05:46 » |
|
А что за побочные эффекты ? Кстати, "x","X" (большой X - для заглавных хексов) - тоже целые
|
|
« Последнее редактирование: 13-12-2009 05:48 от Алексей1153++ »
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #14 : 13-12-2009 07:40 » |
|
Алексей1153++, нет, u, o и x - это уже желающие пусть сами дописывают. Побочные эффекты в том, что функция вычисления ширины теперь тоже берёт аргумент, и, в частности, поэтому получение значения должно быть обязательно после вызова функции вычисления ширины, и два раза повторяется проверка на выход за пределы массива. Некрасиво это. Чуть переделал, вынеся логику контроля типов и выхода за пределы в отедльную функцию - генератор аргументов. Но всё равно сохраняется неочевидность внутри replace, что получение значения должно следовать обязательно после вычисления ширины. Несовершенный код. Нужно, чтобы аргументы извлекались в одном месте, но извлечение аргумента при вычислении ширины возможно лишь в результате анализа. Размазывать логику вычисления ширины по двум функциям тоже нехорошо. Сливать всё в одну функцию replace - это получить перегруженный деталями код. У кого какие предложения будут? Решение выглядело бы изящнее, если бы вместо регулярных выражений, предоставляющих неупорядоченное множество групп, был бы написан собственный автомат разбора, с явной последовательностью состояний, но это было бы более громоздко. Так и хочется написать что-то вроде анонимного метода или блока кода, передаваемого как аргумент функции. Добавил делегат в параметры функции вычисления ширины, чтобы синтаксически указать в replace зависимость функции вычисления ширины от стека аргументов. Всё равно не изящно.
|
|
« Последнее редактирование: 13-12-2009 08:41 от Dimka »
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #15 : 13-12-2009 09:00 » |
|
Глянул, как делается в CString - там тоже для определения длины буфера назначения код парсинга прогоняется два раза.
А может быть, сделать мини-кеш, буквально на одну строчку - если входная строка такая же, как предыдущая, то на выход уходит сразу готовая (также предыдущая) выходная строка ?
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #16 : 13-12-2009 09:51 » |
|
По-моему с длиной строк в .NET проблем нет. Единственно, что складывание строк в циклах не является оптимальными - лучше использовать StringBuilder.
По поводу кэша была идея: сформировать сначала форматную строчку в стиле .NET, а потом к ней применять аргументы. Но контроль типов идёт лесом, а, с другой стороны, такое решение лучше делать по схеме Regex (т.е. формат задать в конструкторе, а потом печатать с его помощью аргументы), нежели статическим методом - усложняется использование. Поэтому такой вариант выгоден лишь для более узкой задачи - печати разных данных по одному формату.
|
|
« Последнее редактирование: 13-12-2009 09:52 от Dimka »
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
|