I need for someone to be able to put some text into a page and then this gets sent to the server, saved in the database, and else where this text is put into a javascript variable.
Basically like this:
Write("var myVar=\""+MyData+"\";");
What is the best way of escaping this data? Is there anything out there already to deal with things like ' and " and new lines? Is base64 my only option?
My serverside framework/language is ASP.Net/C#
You should use WPL:
Write("var myVar=" + Encoder.JavaScriptEncode(MyData, true) + ";");
if you don't want to reference the library, you can use the following function (adapted from the .Net source):
public static void QuoteString(this string value, StringBuilder b) {
if (String.IsNullOrEmpty(value))
return "";
var b = new StringBuilder();
int startIndex = 0;
int count = 0;
for (int i = 0; i < value.Length; i++) {
char c = value[i];
// Append the unhandled characters (that do not require special treament)
// to the string builder when special characters are detected.
if (c == '\r' || c == '\t' || c == '\"' || c == '\'' || c == '<' || c == '>' ||
c == '\\' || c == '\n' || c == '\b' || c == '\f' || c < ' ') {
if (b == null) {
b = new StringBuilder(value.Length + 5);
}
if (count > 0) {
b.Append(value, startIndex, count);
}
startIndex = i + 1;
count = 0;
}
switch (c) {
case '\r':
b.Append("\\r");
break;
case '\t':
b.Append("\\t");
break;
case '\"':
b.Append("\\\"");
break;
case '\\':
b.Append("\\\\");
break;
case '\n':
b.Append("\\n");
break;
case '\b':
b.Append("\\b");
break;
case '\f':
b.Append("\\f");
break;
case '\'':
case '>':
case '<':
AppendCharAsUnicode(b, c);
break;
default:
if (c < ' ') {
AppendCharAsUnicode(b, c);
} else {
count++;
}
break;
}
}
if (b == null) {
b.Append(value);
}
if (count > 0) {
b.Append(value, startIndex, count);
}
return b.ToString();
}
Related
I recently decided to type up some code in C sharp that determines the outcomes for a Tic-Tac-Toe game.
In my code, I received the results:
Total number of games:77088
214 tied
36533 x wins
40341 o wins
(following the rules of x goes first)
However, my results are vastly contradictory to the results on Google.
According to Wikipedia: Tic-tac-toe there are 138 different game outcomes with 91 won by x, 44 won by o and 3 tied
Most other websites say there are either 9!(352,880) or 3^9(19,683) ways to fill the board.
Through my personal experience and basic testing, I know there are many more than 3 ways to tie a game which invalidates the results presented by Wikipedia. Also, the pure math proposed by other websites witch use 9! and 3^9 fail to take into account games that are ended early.
So, am I mistaken, is everyone else mistaken?
My research:
A game cannot be won in less than 5 turns.
A game has finished once a victor has been found.
A victor is determined by a match of 3 vertically, horizontally or diagonally across any 3 cells.
My testing code : c sharp - ran in Unity
void Start()
{
int x=0;
int o=0;
int t=0;
int victor;
for (int i = 0; i < 9; i++)
{//turn 1 - no win
for (int b = 0; b < 9; b++)
{//turn 2 - no win
if (b == i) continue;
for (int c = 0; c < 9; c++)
{//turn 3 - no win
if (c == i || c == b) continue;
for (int d = 0; d < 9; d++)
{//tunr 4 - no win
if (d == i || d == b || d == c) continue;
for (int e = 0; e < 9; e++)
{//turn 5 - possible winner
if (e == i || e == b || e == c || e == d) continue;
TicTacToeGame ticTacToeGame = new TicTacToeGame();
ticTacToeGame.SetMove(i);
ticTacToeGame.SetMove(b);
ticTacToeGame.SetMove(c);
ticTacToeGame.SetMove(d);
victor = ticTacToeGame.SetMove(e);
if (victor > 0)
{
Debug.Log(ticTacToeGame.GetGame());
Debug.Log("The victor is " + (victor == 1 ? 'X' : (victor == 2 ? 'O' : ' ')));
switch (victor)
{
case 0:
t++;
break;
case 1:
x++;
break;
case 2:
o++;
break;
}
continue;
}
for (int f = 0; f < 9; f++)
{
if (f == i || f == b || f == c || f == d || f == e) continue;
TicTacToeGame ticTacToeGame6 = new TicTacToeGame();
ticTacToeGame.SetMove(i);
ticTacToeGame.SetMove(b);
ticTacToeGame.SetMove(c);
ticTacToeGame.SetMove(d);
ticTacToeGame.SetMove(e);
victor = ticTacToeGame.SetMove(f);
if (victor > 0)
{
Debug.Log(ticTacToeGame.GetGame());
Debug.Log("The victor is " + (victor == 1 ? 'X' : (victor == 2 ? 'O' : ' ')));
switch (victor)
{
case 0:
t++;
break;
case 1:
x++;
break;
case 2:
o++;
break;
}
continue;
}
for (int g = 0; g < 9; g++)
{
if (g == i || g == b || g == c || g == d || g == e || g==f) continue;
TicTacToeGame ticTacToeGame7 = new TicTacToeGame();
ticTacToeGame.SetMove(i);
ticTacToeGame.SetMove(b);
ticTacToeGame.SetMove(c);
ticTacToeGame.SetMove(d);
ticTacToeGame.SetMove(e);
ticTacToeGame.SetMove(f);
victor = ticTacToeGame.SetMove(g);
if (victor > 0)
{
Debug.Log(ticTacToeGame.GetGame());
Debug.Log("The victor is " + (victor == 1 ? 'X' : (victor == 2 ? 'O' : ' ')));
switch (victor)
{
case 0:
t++;
break;
case 1:
x++;
break;
case 2:
o++;
break;
}
continue;
}
for (int h = 0; h < 9; h++)
{
if (h == i || h == b || h == c || h == d || h == e || h == f || h==g) continue;
TicTacToeGame ticTacToeGame8 = new TicTacToeGame();
ticTacToeGame.SetMove(i);
ticTacToeGame.SetMove(b);
ticTacToeGame.SetMove(c);
ticTacToeGame.SetMove(d);
ticTacToeGame.SetMove(e);
ticTacToeGame.SetMove(f);
ticTacToeGame.SetMove(g);
victor = ticTacToeGame.SetMove(h);
if (victor > 0)
{
Debug.Log(ticTacToeGame.GetGame());
Debug.Log("The victor is " + (victor == 1 ? 'X' : (victor == 2 ? 'O' : ' ')));
switch (victor)
{
case 0:
t++;
break;
case 1:
x++;
break;
case 2:
o++;
break;
}
continue;
}
for (int j = 0; j < 9; j++)
{
if (j == i || j == b || j == c || j == d || j == e || j == f || j == g || j==h) continue;
TicTacToeGame ticTacToeGame9 = new TicTacToeGame();
ticTacToeGame.SetMove(i);
ticTacToeGame.SetMove(b);
ticTacToeGame.SetMove(c);
ticTacToeGame.SetMove(d);
ticTacToeGame.SetMove(e);
ticTacToeGame.SetMove(f);
ticTacToeGame.SetMove(g);
ticTacToeGame.SetMove(h);
victor = ticTacToeGame.SetMove(j);
Debug.Log(ticTacToeGame.GetGame());
Debug.Log("The victor is " + (victor == 1 ? "X" : (victor == 2 ? "O" : "Tied")));
switch (victor)
{
case 0:
t++;
break;
case 1:
x++;
break;
case 2:
o++;
break;
}
continue;
}
}
}
}
}
}
}
}
}
Debug.Log(t + " tied\n" + x + " x\n" + o + " o");
Debug.Log("Total : " + (t + x + o));
}
private class TicTacToeGame
{
int[] gameBoard = new int[9];
bool turn = true;
int victor = 0;
bool isActive = false;
public TicTacToeGame()
{
isActive = true;
}
public int SetMove(int i)
{
if (!isActive) return victor;
if (i < 0 || i > 8) throw new Exception("Invalid move : " + i);
gameBoard[i] = turn ? 1 : 2;
turn = !turn;
victor = CheckVictory();
if (victor!=0)
{
isActive = false;
}
return victor;
}
private int CheckVictory()
{
if(IsEqual(gameBoard[0], gameBoard[1], gameBoard[2]))
{
return gameBoard[0];
}
if (IsEqual(gameBoard[3], gameBoard[4], gameBoard[5]))
{
return gameBoard[3];
}
if (IsEqual(gameBoard[6], gameBoard[7], gameBoard[8]))
{
return gameBoard[6];
}
if (IsEqual(gameBoard[0], gameBoard[3], gameBoard[6]))
{
return gameBoard[6];
}
if (IsEqual(gameBoard[1], gameBoard[4], gameBoard[7]))
{
return gameBoard[1];
}
if (IsEqual(gameBoard[2], gameBoard[5], gameBoard[8]))
{
return gameBoard[2];
}
if (IsEqual(gameBoard[0], gameBoard[4], gameBoard[8]))
{
return gameBoard[0];
}
if (IsEqual(gameBoard[6], gameBoard[4], gameBoard[2]))
{
return gameBoard[6];
}
return 0;
}
private bool IsEqual(int a, int b, int c)
{
if (a == 0 || b == 0 || c == 0) return false;
return a == b && a == c;
}
public string GetGame()
{
return string.Format("{0}|{1}|{2}\n" +
"-----\n" +
"{3}|{4}|{5}\n" +
"-----\n" +
"{6}|{7}|{8}",
IntToChar(gameBoard[0]),
IntToChar(gameBoard[1]),
IntToChar(gameBoard[2]),
IntToChar(gameBoard[3]),
IntToChar(gameBoard[4]),
IntToChar(gameBoard[5]),
IntToChar(gameBoard[6]),
IntToChar(gameBoard[7]),
IntToChar(gameBoard[8]));
}
private char IntToChar(int i)
{
return i == 1 ? 'X' : (i == 2 ? 'O' : ' ');
}
}
Update ::
After reading some comments, I realized that there was a flaw in my code as it wasn't checking all outcomes but now that should be rectified. One issue that is not addressed would be rotations or reflections but if this data is to be used in practice then rotations and reflections should be included otherwise the computer will have to rotate and reflect every move before making a decision which may take as long or longer than running through an array of outcomes.
Either way, my data is still widely contradictory to results found online.
Are there any classes/functions available to be used for easy JSON escaping? I'd rather not have to write my own.
I use System.Web.HttpUtility.JavaScriptStringEncode
string quoted = HttpUtility.JavaScriptStringEncode(input);
For those using the very popular Json.Net project from Newtonsoft the task is trivial:
using Newtonsoft.Json;
....
var s = JsonConvert.ToString(#"a\b");
Console.WriteLine(s);
....
This code prints:
"a\\b"
That is, the resulting string value contains the quotes as well as the escaped backslash.
Building on the answer by Dejan, what you can do is import System.Web.Helpers .NET Framework assembly, then use the following function:
static string EscapeForJson(string s) {
string quoted = System.Web.Helpers.Json.Encode(s);
return quoted.Substring(1, quoted.Length - 2);
}
The Substring call is required, since Encode automatically surrounds strings with double quotes.
Yep, just add the following function to your Utils class or something:
public static string cleanForJSON(string s)
{
if (s == null || s.Length == 0) {
return "";
}
char c = '\0';
int i;
int len = s.Length;
StringBuilder sb = new StringBuilder(len + 4);
String t;
for (i = 0; i < len; i += 1) {
c = s[i];
switch (c) {
case '\\':
case '"':
sb.Append('\\');
sb.Append(c);
break;
case '/':
sb.Append('\\');
sb.Append(c);
break;
case '\b':
sb.Append("\\b");
break;
case '\t':
sb.Append("\\t");
break;
case '\n':
sb.Append("\\n");
break;
case '\f':
sb.Append("\\f");
break;
case '\r':
sb.Append("\\r");
break;
default:
if (c < ' ') {
t = "000" + String.Format("X", c);
sb.Append("\\u" + t.Substring(t.Length - 4));
} else {
sb.Append(c);
}
break;
}
}
return sb.ToString();
}
I have used following code to escape the string value for json.
You need to add your '"' to the output of the following code:
public static string EscapeStringValue(string value)
{
const char BACK_SLASH = '\\';
const char SLASH = '/';
const char DBL_QUOTE = '"';
var output = new StringBuilder(value.Length);
foreach (char c in value)
{
switch (c)
{
case SLASH:
output.AppendFormat("{0}{1}", BACK_SLASH, SLASH);
break;
case BACK_SLASH:
output.AppendFormat("{0}{0}", BACK_SLASH);
break;
case DBL_QUOTE:
output.AppendFormat("{0}{1}",BACK_SLASH,DBL_QUOTE);
break;
default:
output.Append(c);
break;
}
}
return output.ToString();
}
In .Net Core 3+ and .Net 5+:
string escapedJsonString = JsonEncodedText.Encode(jsonString);
The methods offered here are faulty.
Why venture that far when you could just use System.Web.HttpUtility.JavaScriptEncode ?
If you're on a lower framework, you can just copy paste it from mono
Courtesy of the mono-project #
https://github.com/mono/mono/blob/master/mcs/class/System.Web/System.Web/HttpUtility.cs
public static string JavaScriptStringEncode(string value, bool addDoubleQuotes)
{
if (string.IsNullOrEmpty(value))
return addDoubleQuotes ? "\"\"" : string.Empty;
int len = value.Length;
bool needEncode = false;
char c;
for (int i = 0; i < len; i++)
{
c = value[i];
if (c >= 0 && c <= 31 || c == 34 || c == 39 || c == 60 || c == 62 || c == 92)
{
needEncode = true;
break;
}
}
if (!needEncode)
return addDoubleQuotes ? "\"" + value + "\"" : value;
var sb = new System.Text.StringBuilder();
if (addDoubleQuotes)
sb.Append('"');
for (int i = 0; i < len; i++)
{
c = value[i];
if (c >= 0 && c <= 7 || c == 11 || c >= 14 && c <= 31 || c == 39 || c == 60 || c == 62)
sb.AppendFormat("\\u{0:x4}", (int)c);
else switch ((int)c)
{
case 8:
sb.Append("\\b");
break;
case 9:
sb.Append("\\t");
break;
case 10:
sb.Append("\\n");
break;
case 12:
sb.Append("\\f");
break;
case 13:
sb.Append("\\r");
break;
case 34:
sb.Append("\\\"");
break;
case 92:
sb.Append("\\\\");
break;
default:
sb.Append(c);
break;
}
}
if (addDoubleQuotes)
sb.Append('"');
return sb.ToString();
}
This can be compacted into
// https://github.com/mono/mono/blob/master/mcs/class/System.Json/System.Json/JsonValue.cs
public class SimpleJSON
{
private static bool NeedEscape(string src, int i)
{
char c = src[i];
return c < 32 || c == '"' || c == '\\'
// Broken lead surrogate
|| (c >= '\uD800' && c <= '\uDBFF' &&
(i == src.Length - 1 || src[i + 1] < '\uDC00' || src[i + 1] > '\uDFFF'))
// Broken tail surrogate
|| (c >= '\uDC00' && c <= '\uDFFF' &&
(i == 0 || src[i - 1] < '\uD800' || src[i - 1] > '\uDBFF'))
// To produce valid JavaScript
|| c == '\u2028' || c == '\u2029'
// Escape "</" for <script> tags
|| (c == '/' && i > 0 && src[i - 1] == '<');
}
public static string EscapeString(string src)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
int start = 0;
for (int i = 0; i < src.Length; i++)
if (NeedEscape(src, i))
{
sb.Append(src, start, i - start);
switch (src[i])
{
case '\b': sb.Append("\\b"); break;
case '\f': sb.Append("\\f"); break;
case '\n': sb.Append("\\n"); break;
case '\r': sb.Append("\\r"); break;
case '\t': sb.Append("\\t"); break;
case '\"': sb.Append("\\\""); break;
case '\\': sb.Append("\\\\"); break;
case '/': sb.Append("\\/"); break;
default:
sb.Append("\\u");
sb.Append(((int)src[i]).ToString("x04"));
break;
}
start = i + 1;
}
sb.Append(src, start, src.Length - start);
return sb.ToString();
}
}
I ran speed tests on some of these answers for a long string and a short string. Clive Paterson's code won by a good bit, presumably because the others are taking into account serialization options. Here are my results:
Apple Banana
System.Web.HttpUtility.JavaScriptStringEncode: 140ms
System.Web.Helpers.Json.Encode: 326ms
Newtonsoft.Json.JsonConvert.ToString: 230ms
Clive Paterson: 108ms
\\some\long\path\with\lots\of\things\to\escape\some\long\path\t\with\lots\of\n\things\to\escape\some\long\path\with\lots\of\"things\to\escape\some\long\path\with\lots"\of\things\to\escape
System.Web.HttpUtility.JavaScriptStringEncode: 2849ms
System.Web.Helpers.Json.Encode: 3300ms
Newtonsoft.Json.JsonConvert.ToString: 2827ms
Clive Paterson: 1173ms
And here is the test code:
public static void Main(string[] args)
{
var testStr1 = "Apple Banana";
var testStr2 = #"\\some\long\path\with\lots\of\things\to\escape\some\long\path\t\with\lots\of\n\things\to\escape\some\long\path\with\lots\of\""things\to\escape\some\long\path\with\lots""\of\things\to\escape";
foreach (var testStr in new[] { testStr1, testStr2 })
{
var results = new Dictionary<string,List<long>>();
for (var n = 0; n < 10; n++)
{
var count = 1000 * 1000;
var sw = Stopwatch.StartNew();
for (var i = 0; i < count; i++)
{
var s = System.Web.HttpUtility.JavaScriptStringEncode(testStr);
}
var t = sw.ElapsedMilliseconds;
results.GetOrCreate("System.Web.HttpUtility.JavaScriptStringEncode").Add(t);
sw = Stopwatch.StartNew();
for (var i = 0; i < count; i++)
{
var s = System.Web.Helpers.Json.Encode(testStr);
}
t = sw.ElapsedMilliseconds;
results.GetOrCreate("System.Web.Helpers.Json.Encode").Add(t);
sw = Stopwatch.StartNew();
for (var i = 0; i < count; i++)
{
var s = Newtonsoft.Json.JsonConvert.ToString(testStr);
}
t = sw.ElapsedMilliseconds;
results.GetOrCreate("Newtonsoft.Json.JsonConvert.ToString").Add(t);
sw = Stopwatch.StartNew();
for (var i = 0; i < count; i++)
{
var s = cleanForJSON(testStr);
}
t = sw.ElapsedMilliseconds;
results.GetOrCreate("Clive Paterson").Add(t);
}
Console.WriteLine(testStr);
foreach (var result in results)
{
Console.WriteLine(result.Key + ": " + Math.Round(result.Value.Skip(1).Average()) + "ms");
}
Console.WriteLine();
}
Console.ReadLine();
}
I would also recommend using the JSON.NET library mentioned, but if you have to escape unicode characters (e.g. \uXXXX format) in the resulting JSON string, you may have to do it yourself. Take a look at Converting Unicode strings to escaped ascii string for an example.
I nice one-liner, used JsonConvert as others have but added substring to remove the added quotes and backslash.
var escapedJsonString = JsonConvert.ToString(JsonString).Substring(1, JsonString.Length - 2);
What about System.Web.Helpers.Json.Encode(...) (see http://msdn.microsoft.com/en-us/library/system.web.helpers.json.encode(v=vs.111).aspx)?
String.Format("X", c);
That just outputs: X
Try this instead:
string t = ((int)c).ToString("X");
sb.Append("\\u" + t.PadLeft(4, '0'));
There's a Json library at Codeplex
I chose to use System.Web.Script.Serialization.JavaScriptSerializer.
I have a small static helper class defined as follows:
internal static partial class Serialization
{
static JavaScriptSerializer serializer;
static Serialization()
{
serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = Int32.MaxValue;
}
public static string ToJSON<T>(T obj)
{
return serializer.Serialize(obj);
}
public static T FromJSON<T>(string data)
{
if (Common.IsEmpty(data))
return default(T);
else
return serializer.Deserialize<T>(data);
}
}
To serialize anything I just call Serialization.ToJSON(itemToSerialize)
To deserialize I just call Serialization.FromJSON<T>(jsonValueOfTypeT)
I want to convert this () into this ) (
Like for example (1+2) to ) 1+2(
i have tried this
char[] cArray = text.ToCharArray();
for (int i = 0; i < text.Length; i++)
{
if (cArray[i] == '(')
{
cArray[i] = ')';
}
if (cArray[i] == ')')
{
cArray[i] = '(';
}
}
string p=new string(cArray);
return p;
but it does not work
The problem is that after you've changed ( to ), the second if statement will immediately be true, so the character is flipped back again. The reverse isn't true though - if you start off with ) that will be flipped by the second statement, but then it won't be flipped back again. The net result is that all ) characters will be flipped to (, but that's all. So an input of "(((foo)))" would return "(((foo(((.
The simplest way to fix that is to use an else statement:
char[] cArray = text.ToCharArray();
for (int i = 0; i < text.Length; i++)
{
if (cArray[i] == '(')
{
cArray[i] = ')';
}
else if (cArray[i] == ')')
{
cArray[i] = '(';
}
}
return new string(cArray);
Alternatively, you could use a switch statement, which would be simpler if you had a lot of different cases to consider:
char[] cArray = text.ToCharArray();
for (int i = 0; i < text.Length; i++)
{
switch (cArray[i])
{
case '(':
cArray[i] = ')';
break;
case ')':
cArray[i] = '(';
break;
// No need for a default as it would be a no-op
}
}
return new string(cArray);
A switch statement will only evaluate the expression once (on each iteration, of course) so you don't need to worry about the two cases interfering with each other.
Use else before the second if as follows:
char[] cArray = text.ToCharArray();
for (int i = 0; i < text.Length; i++)
{
if (cArray[i] == '(')
{
cArray[i] = ')';
}
else if (cArray[i] == ')')
{
cArray[i] = '(';
}
}
string p = new string(cArray);
return p;
I'm looking for a way to have one string formatting pattern representation for use in both Java and C#.
Java: http://download.oracle.com/javase/6/docs/api/java/util/Formatter.html
C#: http://msdn.microsoft.com/en-us/library/fht0f5be.aspx
The following Java class implements the most important features of the String.Format
method in C#. It supports the 'X' and 'D' format specifiers, but not 'F', 'G', or 'R'.
package com.upokecenter.util;
import java.util.Locale;
public final class DotNetFormatter {
private DotNetFormatter() {
}
public static String format(String formattedText,
Object... options) {
return format(Locale.getDefault(),formattedText,options);
}
public static String format(Locale locale, String formattedText,
Object... options) {
int numOptions = options.length;
StringBuilder buffer = new StringBuilder();
int i;
for (i = 0; i < formattedText.length(); i++) {
char c = formattedText.charAt(i);
if (c == '{') {
i++;
if (i < formattedText.length()) {
c = formattedText.charAt(i);
if (c == '{') {
buffer.append('{');
} else if (c >= '0' || c <= '9') {
int x = (c - '0');
i++;
while (i < formattedText.length()) {
c = formattedText.charAt(i);
if (c == ':') {
i++;
if (x >= numOptions
|| i >= formattedText.length())
throw new IllegalArgumentException(
"Format string contains a badly numbered argument.");
char formatType = formattedText.charAt(i);
if (formatType == 'x' || formatType == 'X'
|| formatType == 'd'
|| formatType == 'D') {
i++;
if (i >= formattedText.length())
throw new IllegalArgumentException(
"Format string contains a badly numbered argument.");
char formatCount = formattedText.charAt(i);
if (formatCount == '}') {
switch (formatType) {
case 'x':
buffer.append(String.format(locale,
"%x", options[x]));
break;
case 'X':
buffer.append(String.format(locale,
"%X", options[x]));
break;
case 'd':
buffer.append(String.format(locale,
"%d", options[x]));
break;
case 'D':
buffer.append(String.format(locale,
"%d", options[x]));
break;
}
break;
} else if (formatCount < '0'
|| formatCount > '9'
|| (++i) >= formattedText.length())
throw new IllegalArgumentException(
"Format string contains a badly numbered argument.");
else {
if (formattedText.charAt(i) != '}')
throw new IllegalArgumentException(
"Format string contains a badly numbered argument.");
String fmt = "";
switch (formatType) {
case 'x':
fmt = String.format("%%0%cx",
formatCount);
break;
case 'X':
fmt = String.format("%%0%cX",
formatCount);
break;
case 'd':
fmt = String.format("%%0%cd",
formatCount);
break;
case 'D':
fmt = String.format("%%0%cd",
formatCount);
break;
}
buffer.append(String.format(locale,
fmt, options[x]));
break;
}
} else {
throw new IllegalArgumentException(
"Format string contains a badly formatted argument.");
}
} else if (c == '}') {
if (x >= numOptions)
throw new IllegalArgumentException(
"Format string contains a badly numbered argument.");
buffer.append(String.format(locale, "%s",
options[x]));
break;
} else if (c >= '0' || c <= '9') {
i++;
x = x * 10 + (c - '0');
} else {
throw new IllegalArgumentException(
"Format string contains a badly formatted argument.");
}
}
} else {
buffer.append('{');
buffer.append(c);
}
} else {
buffer.append(c);
}
} else {
buffer.append(c);
}
}
return buffer.toString();
}
}
I've written this function...
internal static IEnumerable<KeyValuePair<char?, string>> SplitUnescaped(this string input, char[] separators)
{
int index = 0;
var state = new Stack<char>();
for (int i = 0; i < input.Length; ++i)
{
char c = input[i];
char s = state.Count > 0 ? state.Peek() : default(char);
if (state.Count > 0 && (s == '\\' || (s == '[' && c == ']') || ((s == '"' || s == '\'') && c == s)))
state.Pop();
else if (c == '\\' || c == '[' || c == '"' || c == '\'')
state.Push(c);
if (state.Count == 0 && separators.Contains(c))
{
yield return new KeyValuePair<char?, string>(c, input.Substring(index, i - index));
index = i + 1;
}
}
yield return new KeyValuePair<char?, string>(null, input.Substring(index));
}
Which splits a string on the given separators, as long as they aren't escaped, in quotes, or in brackets. Seems to work pretty well, but there's one problem with it.
There characters I want to split on include a space:
{ '>', '+', '~', ' ' };
So, given the string
a > b
I want it to split on > and ignore the spaces, but given
a b
I do want it to split on the space.
How can I fix the function?
You could continue to split based on and > and then remove the strings which are empty.
I think this does it...
internal static IEnumerable<KeyValuePair<char?, string>> SplitUnescaped(this string input, char[] separators)
{
int startIndex = 0;
var state = new Stack<char>();
input = input.Trim(separators);
for (int i = 0; i < input.Length; ++i)
{
char c = input[i];
char s = state.Count > 0 ? state.Peek() : default(char);
if (state.Count > 0 && (s == '\\' || (s == '[' && c == ']') || ((s == '"' || s == '\'') && c == s)))
state.Pop();
else if (c == '\\' || c == '[' || c == '"' || c == '\'')
state.Push(c);
else if (state.Count == 0 && separators.Contains(c))
{
int endIndex = i;
while (input[i] == ' ' && separators.Contains(input[i + 1])) { ++i; }
yield return new KeyValuePair<char?, string>(input[i], input.Substring(startIndex, endIndex - startIndex));
while (input[++i] == ' ') { }
startIndex = i;
}
}
yield return new KeyValuePair<char?, string>(null, input.Substring(startIndex));
}
I was trying to push the space onto the stack too before, and then doing some checks against that...but I think this is easier.