首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >最优字符串文字标记算法

最优字符串文字标记算法
EN

Code Review用户
提问于 2014-11-23 20:13:50
回答 1查看 2.8K关注 0票数 6

我有一个令牌程序,其中一个令牌类型恰好是一个C#字符串类型。它以一个TextReader作为它的输入,它是从一个StringReader创建的,它需要支持C#字符串支持的基本内容,例如“乔兄弟”、"joe \“、”这是一个反斜杠\令牌“,当然”这是回车\r\n这是下一行“。

目前我的方法是这样的;

代码语言:javascript
复制
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部分。

_readerTextReader对象,在我的方法的条目处,它位于字符串文本内容中的第一个字符。

有人能找到解析字符串文本的C#类型的最佳解决方案,并优化我的方法吗?

请不要告诉我要使用某些库,因为我不想引入任何库的开销来执行我认为应该很容易用10-20行代码实现的任务。

为了分红,我也想有一个解决方案,我的“多行字符串文字algo”。当给定@“string”时,此方法应该以C#读取字符串的方式读取字符串。目前的情况如下:

代码语言:javascript
复制
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是一个不-不。

EN

回答 1

Code Review用户

回答已采纳

发布于 2014-11-24 00:47:18

在C#中性能的一个经验法则是避免分配。在这种情况下,我们应该尝试删除buffer.ToString ().TrimEnd ('\\'),以及最后出现的对SubstringReplace的调用。

当然,您需要设定性能目标,并根据实际的输入来分析代码。可以合理地期望单行字符串很短(比如200个字符?),所以您已经具备足够的性能,但我将说明另一种方法。

首先,让我们把它变成一个静态方法,这样单元测试就更容易了。正如我在注释中提到的,代码中有一个bug --单元测试可以帮助您捕获这样的bug。

代码语言:javascript
复制
private static string ReadSingleLineStringLiteral(TextReader reader)

和一种方便的测试方法

代码语言:javascript
复制
private static string ReadSingleLineStringLiteral(string line)
{
    using (var reader = new StringReader(line))
    {
        return ReadSingleLineStringLiteral(reader);
    }
}

现在让我们来处理一个简单的情况,这里没有转义字符。让我们编写一些单元测试

代码语言:javascript
复制
Assert.AreEqual("", ReadSingleLineStringLiteral("\""));
Assert.AreEqual("jo dude", ReadSingleLineStringLiteral("jo dude\""));
Assert.Throws<ArgumentException>(() => ReadSingleLineStringLiteral(""));

写最简单的东西让他们通过

代码语言:javascript
复制
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");

单行字符串不应该包含新行,所以让我们添加一个新的测试用例

代码语言:javascript
复制
Assert.Throws<ArgumentException>(() => ReadSingleLineStringLiteral("\n\""));

以及与我们的方法匹配的情况

代码语言:javascript
复制
    switch (c)
    {
    case '"':
        return sb.ToString();
    case '\n':
        throw new ArgumentException("Single line string contains new line");
    default:
        sb.Append((char)c);
        break;
    }

现在让我们继续转到转义序列。

代码语言:javascript
复制
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\""));

我们再加上一个案子

代码语言:javascript
复制
    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;
    }

以及相应的方法

代码语言:javascript
复制
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));
    }
}

有更多的案件需要处理,但这应该足以让你开始。

票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/70671

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档