我有一个令牌程序,其中一个令牌类型恰好是一个C#字符串类型。它以一个TextReader作为它的输入,它是从一个StringReader创建的,它需要支持C#字符串支持的基本内容,例如“乔兄弟”、"joe \“、”这是一个反斜杠\令牌“,当然”这是回车\r\n这是下一行“。
目前我的方法是这样的;
private string ReadSingleLineStringLiteral ()
{
StringBuilder buffer = new StringBuilder ();
int nextChar = _reader.Read ();
while (nextChar != -1) {
buffer.Append ((char)nextChar);
if (nextChar == '"' &&
(buffer.Length == 1 ||
buffer [buffer.Length - 1] != '\\' ||
(buffer.Length - buffer.ToString ().TrimEnd ('\\').Length) % 2 == 0)) {
break;
}
nextChar = _reader.Read ();
}
if (buffer [buffer.Length - 1] != '"')
throw new ArgumentException ("unclosed string literal in hyperlisp file");
return buffer.ToString ().Substring (0, buffer.Length - 1)
.Replace ("\n", "\r\n") // normalizing carriage returns
.Replace ("\r\r\n", "\r\n")
.Replace ("\\\"", "\"")
.Replace ("\\\\", "\\");
}但是,我怀疑这是远远不理想的,除其他外,我讨厌它的buffer.ToString ().TrimEnd ('\\').Length) % 2部分。
_reader是TextReader对象,在我的方法的条目处,它位于字符串文本内容中的第一个字符。
有人能找到解析字符串文本的C#类型的最佳解决方案,并优化我的方法吗?
请不要告诉我要使用某些库,因为我不想引入任何库的开销来执行我认为应该很容易用10-20行代码实现的任务。
为了分红,我也想有一个解决方案,我的“多行字符串文字algo”。当给定@“string”时,此方法应该以C#读取字符串的方式读取字符串。目前的情况如下:
private string ReadMultiLineStringLiteral ()
{
StringBuilder buffer = new StringBuilder ();
int nextChar = _reader.Read ();
while (nextChar != -1) {
buffer.Append ((char)nextChar);
nextChar = _reader.Peek ();
if (nextChar != '"' && (buffer.Length - buffer.ToString ().TrimEnd ('"').Length) % 2 == 1)
break;
nextChar = _reader.Read ();
}
if (buffer.Length == 0 || buffer [buffer.Length - 1] != '"')
throw new ArgumentException ("unclosed multiline string literal in hyperlisp close to end of hyperlisp");
return buffer.ToString ().Substring (0, buffer.Length - 1)
.Replace (@"""""", @"""")
.Replace ("\n", "\r\n") // normalizing carriage returns
.Replace ("\r\r\n", "\r\n");
}这两种方法都从实际字符串内容的第一个字符开始,例如对于string @"xyz“,多行方法的TextReader读取器位置位于"x",对于字符串"abc”,单线字符串文字方法的读取器位置位于"a“。
这可能是不必要的,但显然Regex是一个不-不。
发布于 2014-11-24 00:47:18
在C#中性能的一个经验法则是避免分配。在这种情况下,我们应该尝试删除buffer.ToString ().TrimEnd ('\\'),以及最后出现的对Substring和Replace的调用。
当然,您需要设定性能目标,并根据实际的输入来分析代码。可以合理地期望单行字符串很短(比如200个字符?),所以您已经具备足够的性能,但我将说明另一种方法。
首先,让我们把它变成一个静态方法,这样单元测试就更容易了。正如我在注释中提到的,代码中有一个bug --单元测试可以帮助您捕获这样的bug。
private static string ReadSingleLineStringLiteral(TextReader reader)和一种方便的测试方法
private static string ReadSingleLineStringLiteral(string line)
{
using (var reader = new StringReader(line))
{
return ReadSingleLineStringLiteral(reader);
}
}现在让我们来处理一个简单的情况,这里没有转义字符。让我们编写一些单元测试
Assert.AreEqual("", ReadSingleLineStringLiteral("\""));
Assert.AreEqual("jo dude", ReadSingleLineStringLiteral("jo dude\""));
Assert.Throws<ArgumentException>(() => ReadSingleLineStringLiteral(""));写最简单的东西让他们通过
var sb = new StringBuilder();
for (var c = reader.Read(); c != -1; c = reader.Read())
{
switch (c)
{
case '"':
return sb.ToString();
default:
sb.Append((char)c);
break;
}
}
throw new ArgumentException("Unexpected end of input");单行字符串不应该包含新行,所以让我们添加一个新的测试用例
Assert.Throws<ArgumentException>(() => ReadSingleLineStringLiteral("\n\""));以及与我们的方法匹配的情况
switch (c)
{
case '"':
return sb.ToString();
case '\n':
throw new ArgumentException("Single line string contains new line");
default:
sb.Append((char)c);
break;
}现在让我们继续转到转义序列。
Assert.AreEqual("jo \"dude\"", ReadSingleLineStringLiteral("jo \\\"dude\\\"\""));
Assert.AreEqual("this is a backslash \\ token", ReadSingleLineStringLiteral("this is a backslash \\\\ token\""));
Assert.AreEqual("this is a carriage return \r\nthis is next line", ReadSingleLineStringLiteral("this is a carriage return \\r\\nthis is next line\""));我们再加上一个案子
switch (c)
{
case '"':
return sb.ToString();
case '\\':
AppendEscapeCharacter(reader, sb);
break;
case '\n':
throw new ArgumentException("Single line string contains new line");
default:
sb.Append((char)c);
break;
}以及相应的方法
private static void AppendEscapeCharacter(TextReader reader, StringBuilder sb)
{
var c = reader.Read();
switch (c)
{
case -1:
throw new ArgumentException("Unexpected end of input");
case '"':
case '\'':
case '\\':
sb.Append((char)c);
break;
case 'n':
sb.Append('\n');
break;
case 'r':
sb.Append('\r');
break;
default:
throw new ArgumentException(string.Format("Invalid escape sequence '\\{0}'", (char)c));
}
}有更多的案件需要处理,但这应该足以让你开始。
https://codereview.stackexchange.com/questions/70671
复制相似问题