Function to parse .NET composite string format
$begingroup$
When generating a string representation of expression trees, I would like to render calls to String.Format
with a constant string as the first parameter, as interpolated strings (link). In order to do so, I have to parse the compiler-generated composite format string.
I've written the following function for this purpose, based on the .NET Core parsing function. I am looking primarily for review in:
- correctness, even in edge cases
- clarity / readability
The function returns an array of tuples. Each tuple contains the following elements:
- literal string until the next placeholder
- index of placeholder
- alignment
- item format
If there is literal text after the last placeholder, it will be added as the last tuple of the array (other elements of the tuple will be null
).
The function defines 3 local functions:
advanceChar
-- advances the current position (pos
) by one character, and stores the current character (ch
)
skipWhitespace
-- advances the current position as long as the current character is a space
getNumber
-- gets a multi-digit number starting from the current position; ignores leading/trailing whitespace
public static (string literal, int? index, int? alignment, string itemFormat) ParseFormatString(string format) {
const int indexLimit = 1000000;
const int alignmentLimit = 100000;
int pos = -1;
char ch = 'x0';
int lastPos = format.Length - 1;
var parts = new List<(string literal, int? index, int? alignment, string itemFormat)>();
while (true) {
// Parse literal until argument placeholder
string literal = "";
while (pos < lastPos) {
advanceChar();
if (ch == '}') {
advanceChar();
if (ch == '}') {
literal += '}';
} else {
throw new Exception("Mismatched end brace");
}
} else if (ch == '{') {
advanceChar();
if (ch == '{') {
literal += '{';
} else {
break;
}
} else {
literal += ch;
}
}
if (pos == lastPos) {
if (literal != "") {
parts.Add((literal, (int?)null, (int?)null, (string)null));
}
break;
}
// Parse index section; required
int index = getNumber(indexLimit);
// Parse alignment; optional
int? alignment = null;
if (ch == ',') {
advanceChar();
alignment = getNumber(alignmentLimit, true);
}
// Parse item format; optional
string itemFormat = null;
if (ch == ':') {
advanceChar();
if (ch == '{') {
advanceChar();
if (ch == '{') {
itemFormat += '{';
} else {
throw new Exception("Nested placeholders not allowed");
}
} else if (ch == '}') {
advanceChar();
if (ch=='}') {
itemFormat += '}';
} else {
break;
}
} else {
itemFormat += ch;
}
}
parts.Add((literal, index, alignment, itemFormat));
}
return parts.ToArray();
void advanceChar(bool ignoreEnd = false) {
pos += 1;
if (pos <= lastPos) {
ch = format[pos];
} else if (ignoreEnd) {
ch = 'x0';
} else {
throw new Exception("Unexpected end of text");
}
}
void skipWhitespace() {
while (ch == ' ') {
advanceChar(true);
}
}
int getNumber(int limit, bool allowNegative = false) {
skipWhitespace();
bool isNegative = false;
if (allowNegative && ch == '-') {
isNegative = true;
advanceChar();
}
if (ch < '0' || ch > '9') { throw new Exception("Expected digit"); }
int ret = 0;
do {
ret = ret * 10 + ch - '0';
advanceChar();
} while (ch >= '0' && ch <= '9' && ret < limit);
skipWhitespace();
return ret * (isNegative ? -1 : 1);
}
}
c#
$endgroup$
add a comment |
$begingroup$
When generating a string representation of expression trees, I would like to render calls to String.Format
with a constant string as the first parameter, as interpolated strings (link). In order to do so, I have to parse the compiler-generated composite format string.
I've written the following function for this purpose, based on the .NET Core parsing function. I am looking primarily for review in:
- correctness, even in edge cases
- clarity / readability
The function returns an array of tuples. Each tuple contains the following elements:
- literal string until the next placeholder
- index of placeholder
- alignment
- item format
If there is literal text after the last placeholder, it will be added as the last tuple of the array (other elements of the tuple will be null
).
The function defines 3 local functions:
advanceChar
-- advances the current position (pos
) by one character, and stores the current character (ch
)
skipWhitespace
-- advances the current position as long as the current character is a space
getNumber
-- gets a multi-digit number starting from the current position; ignores leading/trailing whitespace
public static (string literal, int? index, int? alignment, string itemFormat) ParseFormatString(string format) {
const int indexLimit = 1000000;
const int alignmentLimit = 100000;
int pos = -1;
char ch = 'x0';
int lastPos = format.Length - 1;
var parts = new List<(string literal, int? index, int? alignment, string itemFormat)>();
while (true) {
// Parse literal until argument placeholder
string literal = "";
while (pos < lastPos) {
advanceChar();
if (ch == '}') {
advanceChar();
if (ch == '}') {
literal += '}';
} else {
throw new Exception("Mismatched end brace");
}
} else if (ch == '{') {
advanceChar();
if (ch == '{') {
literal += '{';
} else {
break;
}
} else {
literal += ch;
}
}
if (pos == lastPos) {
if (literal != "") {
parts.Add((literal, (int?)null, (int?)null, (string)null));
}
break;
}
// Parse index section; required
int index = getNumber(indexLimit);
// Parse alignment; optional
int? alignment = null;
if (ch == ',') {
advanceChar();
alignment = getNumber(alignmentLimit, true);
}
// Parse item format; optional
string itemFormat = null;
if (ch == ':') {
advanceChar();
if (ch == '{') {
advanceChar();
if (ch == '{') {
itemFormat += '{';
} else {
throw new Exception("Nested placeholders not allowed");
}
} else if (ch == '}') {
advanceChar();
if (ch=='}') {
itemFormat += '}';
} else {
break;
}
} else {
itemFormat += ch;
}
}
parts.Add((literal, index, alignment, itemFormat));
}
return parts.ToArray();
void advanceChar(bool ignoreEnd = false) {
pos += 1;
if (pos <= lastPos) {
ch = format[pos];
} else if (ignoreEnd) {
ch = 'x0';
} else {
throw new Exception("Unexpected end of text");
}
}
void skipWhitespace() {
while (ch == ' ') {
advanceChar(true);
}
}
int getNumber(int limit, bool allowNegative = false) {
skipWhitespace();
bool isNegative = false;
if (allowNegative && ch == '-') {
isNegative = true;
advanceChar();
}
if (ch < '0' || ch > '9') { throw new Exception("Expected digit"); }
int ret = 0;
do {
ret = ret * 10 + ch - '0';
advanceChar();
} while (ch >= '0' && ch <= '9' && ret < limit);
skipWhitespace();
return ret * (isNegative ? -1 : 1);
}
}
c#
$endgroup$
add a comment |
$begingroup$
When generating a string representation of expression trees, I would like to render calls to String.Format
with a constant string as the first parameter, as interpolated strings (link). In order to do so, I have to parse the compiler-generated composite format string.
I've written the following function for this purpose, based on the .NET Core parsing function. I am looking primarily for review in:
- correctness, even in edge cases
- clarity / readability
The function returns an array of tuples. Each tuple contains the following elements:
- literal string until the next placeholder
- index of placeholder
- alignment
- item format
If there is literal text after the last placeholder, it will be added as the last tuple of the array (other elements of the tuple will be null
).
The function defines 3 local functions:
advanceChar
-- advances the current position (pos
) by one character, and stores the current character (ch
)
skipWhitespace
-- advances the current position as long as the current character is a space
getNumber
-- gets a multi-digit number starting from the current position; ignores leading/trailing whitespace
public static (string literal, int? index, int? alignment, string itemFormat) ParseFormatString(string format) {
const int indexLimit = 1000000;
const int alignmentLimit = 100000;
int pos = -1;
char ch = 'x0';
int lastPos = format.Length - 1;
var parts = new List<(string literal, int? index, int? alignment, string itemFormat)>();
while (true) {
// Parse literal until argument placeholder
string literal = "";
while (pos < lastPos) {
advanceChar();
if (ch == '}') {
advanceChar();
if (ch == '}') {
literal += '}';
} else {
throw new Exception("Mismatched end brace");
}
} else if (ch == '{') {
advanceChar();
if (ch == '{') {
literal += '{';
} else {
break;
}
} else {
literal += ch;
}
}
if (pos == lastPos) {
if (literal != "") {
parts.Add((literal, (int?)null, (int?)null, (string)null));
}
break;
}
// Parse index section; required
int index = getNumber(indexLimit);
// Parse alignment; optional
int? alignment = null;
if (ch == ',') {
advanceChar();
alignment = getNumber(alignmentLimit, true);
}
// Parse item format; optional
string itemFormat = null;
if (ch == ':') {
advanceChar();
if (ch == '{') {
advanceChar();
if (ch == '{') {
itemFormat += '{';
} else {
throw new Exception("Nested placeholders not allowed");
}
} else if (ch == '}') {
advanceChar();
if (ch=='}') {
itemFormat += '}';
} else {
break;
}
} else {
itemFormat += ch;
}
}
parts.Add((literal, index, alignment, itemFormat));
}
return parts.ToArray();
void advanceChar(bool ignoreEnd = false) {
pos += 1;
if (pos <= lastPos) {
ch = format[pos];
} else if (ignoreEnd) {
ch = 'x0';
} else {
throw new Exception("Unexpected end of text");
}
}
void skipWhitespace() {
while (ch == ' ') {
advanceChar(true);
}
}
int getNumber(int limit, bool allowNegative = false) {
skipWhitespace();
bool isNegative = false;
if (allowNegative && ch == '-') {
isNegative = true;
advanceChar();
}
if (ch < '0' || ch > '9') { throw new Exception("Expected digit"); }
int ret = 0;
do {
ret = ret * 10 + ch - '0';
advanceChar();
} while (ch >= '0' && ch <= '9' && ret < limit);
skipWhitespace();
return ret * (isNegative ? -1 : 1);
}
}
c#
$endgroup$
When generating a string representation of expression trees, I would like to render calls to String.Format
with a constant string as the first parameter, as interpolated strings (link). In order to do so, I have to parse the compiler-generated composite format string.
I've written the following function for this purpose, based on the .NET Core parsing function. I am looking primarily for review in:
- correctness, even in edge cases
- clarity / readability
The function returns an array of tuples. Each tuple contains the following elements:
- literal string until the next placeholder
- index of placeholder
- alignment
- item format
If there is literal text after the last placeholder, it will be added as the last tuple of the array (other elements of the tuple will be null
).
The function defines 3 local functions:
advanceChar
-- advances the current position (pos
) by one character, and stores the current character (ch
)
skipWhitespace
-- advances the current position as long as the current character is a space
getNumber
-- gets a multi-digit number starting from the current position; ignores leading/trailing whitespace
public static (string literal, int? index, int? alignment, string itemFormat) ParseFormatString(string format) {
const int indexLimit = 1000000;
const int alignmentLimit = 100000;
int pos = -1;
char ch = 'x0';
int lastPos = format.Length - 1;
var parts = new List<(string literal, int? index, int? alignment, string itemFormat)>();
while (true) {
// Parse literal until argument placeholder
string literal = "";
while (pos < lastPos) {
advanceChar();
if (ch == '}') {
advanceChar();
if (ch == '}') {
literal += '}';
} else {
throw new Exception("Mismatched end brace");
}
} else if (ch == '{') {
advanceChar();
if (ch == '{') {
literal += '{';
} else {
break;
}
} else {
literal += ch;
}
}
if (pos == lastPos) {
if (literal != "") {
parts.Add((literal, (int?)null, (int?)null, (string)null));
}
break;
}
// Parse index section; required
int index = getNumber(indexLimit);
// Parse alignment; optional
int? alignment = null;
if (ch == ',') {
advanceChar();
alignment = getNumber(alignmentLimit, true);
}
// Parse item format; optional
string itemFormat = null;
if (ch == ':') {
advanceChar();
if (ch == '{') {
advanceChar();
if (ch == '{') {
itemFormat += '{';
} else {
throw new Exception("Nested placeholders not allowed");
}
} else if (ch == '}') {
advanceChar();
if (ch=='}') {
itemFormat += '}';
} else {
break;
}
} else {
itemFormat += ch;
}
}
parts.Add((literal, index, alignment, itemFormat));
}
return parts.ToArray();
void advanceChar(bool ignoreEnd = false) {
pos += 1;
if (pos <= lastPos) {
ch = format[pos];
} else if (ignoreEnd) {
ch = 'x0';
} else {
throw new Exception("Unexpected end of text");
}
}
void skipWhitespace() {
while (ch == ' ') {
advanceChar(true);
}
}
int getNumber(int limit, bool allowNegative = false) {
skipWhitespace();
bool isNegative = false;
if (allowNegative && ch == '-') {
isNegative = true;
advanceChar();
}
if (ch < '0' || ch > '9') { throw new Exception("Expected digit"); }
int ret = 0;
do {
ret = ret * 10 + ch - '0';
advanceChar();
} while (ch >= '0' && ch <= '9' && ret < limit);
skipWhitespace();
return ret * (isNegative ? -1 : 1);
}
}
c#
c#
asked yesterday
Zev SpitzZev Spitz
1718
1718
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
Problems:
Item format parsing is broken:
"{0:X2}"
fails with an 'Unexpected end of text' exception, while"{0:X2}a"
fails with a 'Mismatched end brace' exception. Both are valid formats.
"{0:}"
also fails with an 'Unexpected end of text' exception, but"{0:}a"
returns an empty array instead. Both are valid formats.
"{0:{{"
and"{0:}}"
are parsed successfully. Both should be rejected as invalid.
Improvements:
- The index and alignment limits seem fairly arbitrary. If they're based on an actual limit it would be a good idea to document that. Also, exceeding those limits results in a misleading 'Mismatched end brace' error.
- I'd recommend using a more specific exception exception type. The existing
FormatException
seems appropriate here. - For repeated string concatenation, a
StringBuilder
is (significantly) more efficient. - The exceptions don't provide much detail. It would be useful to know at what index the problem was detected, or what the parser was expecting when it hit the end of the input.
- The main while loop body is fairly drawn out. If you're using local functions anyway, why not split things up further into a
ParseLiteralPart
andParseFormatPart
function?
$endgroup$
2
$begingroup$
mhmm... based on your findings I'm thinking of flagging the question as not-working-code...
$endgroup$
– t3chb0t
yesterday
$begingroup$
@t3chb0t I've identified and fixed the problems (still working on the improvements); should I edit the code in the question? Should I close this and reopen another question?
$endgroup$
– Zev Spitz
23 hours ago
$begingroup$
@ZevSpitz nope, editing the code is not allowed when there are already answers. What you can do is to either add a self-answer or ask a follow-up question if you'd like to have another review.
$endgroup$
– t3chb0t
13 hours ago
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215417%2ffunction-to-parse-net-composite-string-format%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
Problems:
Item format parsing is broken:
"{0:X2}"
fails with an 'Unexpected end of text' exception, while"{0:X2}a"
fails with a 'Mismatched end brace' exception. Both are valid formats.
"{0:}"
also fails with an 'Unexpected end of text' exception, but"{0:}a"
returns an empty array instead. Both are valid formats.
"{0:{{"
and"{0:}}"
are parsed successfully. Both should be rejected as invalid.
Improvements:
- The index and alignment limits seem fairly arbitrary. If they're based on an actual limit it would be a good idea to document that. Also, exceeding those limits results in a misleading 'Mismatched end brace' error.
- I'd recommend using a more specific exception exception type. The existing
FormatException
seems appropriate here. - For repeated string concatenation, a
StringBuilder
is (significantly) more efficient. - The exceptions don't provide much detail. It would be useful to know at what index the problem was detected, or what the parser was expecting when it hit the end of the input.
- The main while loop body is fairly drawn out. If you're using local functions anyway, why not split things up further into a
ParseLiteralPart
andParseFormatPart
function?
$endgroup$
2
$begingroup$
mhmm... based on your findings I'm thinking of flagging the question as not-working-code...
$endgroup$
– t3chb0t
yesterday
$begingroup$
@t3chb0t I've identified and fixed the problems (still working on the improvements); should I edit the code in the question? Should I close this and reopen another question?
$endgroup$
– Zev Spitz
23 hours ago
$begingroup$
@ZevSpitz nope, editing the code is not allowed when there are already answers. What you can do is to either add a self-answer or ask a follow-up question if you'd like to have another review.
$endgroup$
– t3chb0t
13 hours ago
add a comment |
$begingroup$
Problems:
Item format parsing is broken:
"{0:X2}"
fails with an 'Unexpected end of text' exception, while"{0:X2}a"
fails with a 'Mismatched end brace' exception. Both are valid formats.
"{0:}"
also fails with an 'Unexpected end of text' exception, but"{0:}a"
returns an empty array instead. Both are valid formats.
"{0:{{"
and"{0:}}"
are parsed successfully. Both should be rejected as invalid.
Improvements:
- The index and alignment limits seem fairly arbitrary. If they're based on an actual limit it would be a good idea to document that. Also, exceeding those limits results in a misleading 'Mismatched end brace' error.
- I'd recommend using a more specific exception exception type. The existing
FormatException
seems appropriate here. - For repeated string concatenation, a
StringBuilder
is (significantly) more efficient. - The exceptions don't provide much detail. It would be useful to know at what index the problem was detected, or what the parser was expecting when it hit the end of the input.
- The main while loop body is fairly drawn out. If you're using local functions anyway, why not split things up further into a
ParseLiteralPart
andParseFormatPart
function?
$endgroup$
2
$begingroup$
mhmm... based on your findings I'm thinking of flagging the question as not-working-code...
$endgroup$
– t3chb0t
yesterday
$begingroup$
@t3chb0t I've identified and fixed the problems (still working on the improvements); should I edit the code in the question? Should I close this and reopen another question?
$endgroup$
– Zev Spitz
23 hours ago
$begingroup$
@ZevSpitz nope, editing the code is not allowed when there are already answers. What you can do is to either add a self-answer or ask a follow-up question if you'd like to have another review.
$endgroup$
– t3chb0t
13 hours ago
add a comment |
$begingroup$
Problems:
Item format parsing is broken:
"{0:X2}"
fails with an 'Unexpected end of text' exception, while"{0:X2}a"
fails with a 'Mismatched end brace' exception. Both are valid formats.
"{0:}"
also fails with an 'Unexpected end of text' exception, but"{0:}a"
returns an empty array instead. Both are valid formats.
"{0:{{"
and"{0:}}"
are parsed successfully. Both should be rejected as invalid.
Improvements:
- The index and alignment limits seem fairly arbitrary. If they're based on an actual limit it would be a good idea to document that. Also, exceeding those limits results in a misleading 'Mismatched end brace' error.
- I'd recommend using a more specific exception exception type. The existing
FormatException
seems appropriate here. - For repeated string concatenation, a
StringBuilder
is (significantly) more efficient. - The exceptions don't provide much detail. It would be useful to know at what index the problem was detected, or what the parser was expecting when it hit the end of the input.
- The main while loop body is fairly drawn out. If you're using local functions anyway, why not split things up further into a
ParseLiteralPart
andParseFormatPart
function?
$endgroup$
Problems:
Item format parsing is broken:
"{0:X2}"
fails with an 'Unexpected end of text' exception, while"{0:X2}a"
fails with a 'Mismatched end brace' exception. Both are valid formats.
"{0:}"
also fails with an 'Unexpected end of text' exception, but"{0:}a"
returns an empty array instead. Both are valid formats.
"{0:{{"
and"{0:}}"
are parsed successfully. Both should be rejected as invalid.
Improvements:
- The index and alignment limits seem fairly arbitrary. If they're based on an actual limit it would be a good idea to document that. Also, exceeding those limits results in a misleading 'Mismatched end brace' error.
- I'd recommend using a more specific exception exception type. The existing
FormatException
seems appropriate here. - For repeated string concatenation, a
StringBuilder
is (significantly) more efficient. - The exceptions don't provide much detail. It would be useful to know at what index the problem was detected, or what the parser was expecting when it hit the end of the input.
- The main while loop body is fairly drawn out. If you're using local functions anyway, why not split things up further into a
ParseLiteralPart
andParseFormatPart
function?
answered yesterday
Pieter WitvoetPieter Witvoet
6,429826
6,429826
2
$begingroup$
mhmm... based on your findings I'm thinking of flagging the question as not-working-code...
$endgroup$
– t3chb0t
yesterday
$begingroup$
@t3chb0t I've identified and fixed the problems (still working on the improvements); should I edit the code in the question? Should I close this and reopen another question?
$endgroup$
– Zev Spitz
23 hours ago
$begingroup$
@ZevSpitz nope, editing the code is not allowed when there are already answers. What you can do is to either add a self-answer or ask a follow-up question if you'd like to have another review.
$endgroup$
– t3chb0t
13 hours ago
add a comment |
2
$begingroup$
mhmm... based on your findings I'm thinking of flagging the question as not-working-code...
$endgroup$
– t3chb0t
yesterday
$begingroup$
@t3chb0t I've identified and fixed the problems (still working on the improvements); should I edit the code in the question? Should I close this and reopen another question?
$endgroup$
– Zev Spitz
23 hours ago
$begingroup$
@ZevSpitz nope, editing the code is not allowed when there are already answers. What you can do is to either add a self-answer or ask a follow-up question if you'd like to have another review.
$endgroup$
– t3chb0t
13 hours ago
2
2
$begingroup$
mhmm... based on your findings I'm thinking of flagging the question as not-working-code...
$endgroup$
– t3chb0t
yesterday
$begingroup$
mhmm... based on your findings I'm thinking of flagging the question as not-working-code...
$endgroup$
– t3chb0t
yesterday
$begingroup$
@t3chb0t I've identified and fixed the problems (still working on the improvements); should I edit the code in the question? Should I close this and reopen another question?
$endgroup$
– Zev Spitz
23 hours ago
$begingroup$
@t3chb0t I've identified and fixed the problems (still working on the improvements); should I edit the code in the question? Should I close this and reopen another question?
$endgroup$
– Zev Spitz
23 hours ago
$begingroup$
@ZevSpitz nope, editing the code is not allowed when there are already answers. What you can do is to either add a self-answer or ask a follow-up question if you'd like to have another review.
$endgroup$
– t3chb0t
13 hours ago
$begingroup$
@ZevSpitz nope, editing the code is not allowed when there are already answers. What you can do is to either add a self-answer or ask a follow-up question if you'd like to have another review.
$endgroup$
– t3chb0t
13 hours ago
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215417%2ffunction-to-parse-net-composite-string-format%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown