【问题标题】:Ignoring a Rogue quote inside Double quotes忽略双引号内的流氓引号
【发布时间】:2013-08-08 08:16:37
【问题描述】:

我有以下用于 csv 解析器的代码

string input = wholeFile;
IList<string> wholeFileArray = new List<string>();
int start = 0;
bool inQuotes = false;
for (int current = 0; current < input.Length; current++)
{
   // test each character before and after to determine if it is a valid quote, or a quote within a quote.
   int test_backward = (current == 0 ? 1 : current) - 1;
   int test_forward = (current == input.Length - 1 ? input.Length - 2 : current) + 1;
   bool valid_quote = input[test_backward] == ',' || input[test_forward] == ',' || input[test_forward] == '\r';
    if (input[current] == '\"') // toggle state
    {
        inQuotes = !inQuotes;
    }
    bool atLastChar = (current == input.Length - 1);
    if (atLastChar)
    {
        wholeFileArray.Add(input.Substring(start));
    }
    else if (input[current] == ',' && !inQuotes)
    {
        wholeFileArray.Add(input.Substring(start, current - start));
        start = current + 1;
    }
}

如果, 不在这样的双引号"something,foobar" 字符串内,它会接受一个字符串并将其拆分到,

我的问题是我的字符串中的流氓" 正在搞乱我的整个过程。

例如:"bla bla","bla bla2",3,4,"5","bla"bla","End" 结果

  • “bla bla”
  • "bla bla2"
  • 3
  • 4
  • “5”
  • "bla"bla","End"

如何更改我的代码以允许流氓"

“有效”右引号后始终跟逗号 (,) 或控制换行符

已添加 这似乎解决了它

// test each character before and after to determine if it is a valid quote, or a quote within a quote.
int test_backward = (current == 0 ? 1 : current) - 1;
int test_forward = (current == input.Length - 1 ? input.Length - 2 : current) + 1;
bool valid_quote = input[test_backward] == ',' || input[test_forward] == ',' || input[test_forward] == '\r';

【问题讨论】:

  • 就像尝试确定黑白引号时的颜色一样有趣,我决定更正拼写。
  • 您的示例中唯一可靠的模式是“有效”右引号后始终跟逗号 (,)。你也许可以通过检查来让它工作
  • @musefan 我应该提到这是一个 csv 解析器,所以它也需要在行尾匹配
  • @Josefvz:问题是输入无效。没有人可以期望解析器只处理无效数据。内部引号应该被转义。你能做的最好的事情就是像我说的那样,在每个潜在的关闭引号之后,向前看几个字符,然后确定你是否仍然在一个字符串中。即,如果您在可能关闭的引号和下一个引号(或行尾)之间只有一个逗号或空格,那么它就是一个有效的关闭引号。如果您发现任何其他字符,假设您仍在字符串中。
  • 明天你会回来对我们说...我有双重流氓:"bla",bla"...我该怎么办?

标签: c# .net string


【解决方案1】:

试试这样的:

if (input[current] == '"' && // 1
    (!inQuotes || // 2
    current + 1 == input.Length || // 3
    input[current + 1] == '\r' || // 4
    input[current + 1] == '\n' || // 5
        (input[current + 1] == ',' && // 6
            (current + 2 == input.Length || // 7
            input[current + 2] == '\r' || // 8
            input[current + 2] == '\n' || // 9
            input[current + 2] == '"' || // 10
                (input[current + 2] >= '0' && input[current + 2] <= '9'))))) // 11
// toggle state

但请注意,您想要做的事情在各个概念层面上都是错误的。

正确的引号是开引号 2 或作为字符串最后一个字符的引号 3 或后跟 \r 4 \n 5 或后跟, 6 又是字符串的最后一个字符7 或后跟 \r 8\n 9 或引用 " 10 或由一个数字 11.

【讨论】:

  • 谢谢,我试试这个。我知道它在各种概念层面上都是错误的,我得到的文件是 EVIL,而那些家伙“不能”改变它。所以我必须解决它
【解决方案2】:

如果您可以选择基于 bnf 执行此操作,这是一个相当简单的语法。下面是使用 fsyacc 的样子(又可以从 C# 中使用)

start: lines
lines: line lines {$1::$2}
     | {[]}

line: val vals {$1::$2}
    |  {[]}

val : QUOTE STR QUOTE COMMA {$2}
    | QUOTE STR QUOTE STR QUOTE COMMA { $2 + "\"" + $4 }
    | QUOTE STR QUOTE EOL {$2}
    | QUOTE STR QUOTE STR QUOTE EOL { $2 + "\"" + $4 }
    | QUOTE STR QUOTE EOF {$2}
    | QUOTE STR QUOTE STR QUOTE EOF { $2 + "\"" + $4 }

产生式val 也有点表明这是一个不干净的语法,因为您需要下一个标记来确定要做什么。如果可以要求每行以换行符结尾(包括最后一行),则 val 可以简化为四而不是六,并且要求每行以逗号结尾会将其简化为两行。通过这种方式可以简化很多语法(要求每个语句都以特定字符结尾),这是 c++ 使用的方式;

【讨论】:

    【解决方案3】:

    作为替代方案,只要引号内没有,,您可以查看Microsoft.VisualBasic.FileIO.TextFieldParser

    以下代码sn-p:

    using Microsoft.VisualBasic.FileIO;
    
    
    using (TextFieldParser parser = new TextFieldParser(fileName))
    {
    
        parser.Delimiters = new string[] { "," };
    
        while (!parser.EndOfData)
        {
            string[] fields = parser.ReadFields();   
        }
    }
    

    上面的代码 sn-p 生成一个数组,示例行如下:

    “布拉布拉” “布拉布拉2” 3 4 5 “布拉”布拉 “结尾”

    显然这需要适应您的代码,这不是最佳解决方案(特别是如果您在引号之间有,),但它可能比尝试处理任意数量的“流氓”引号更容易。

    【讨论】:

    • 不幸的是,我在引号内有,
    • @Josefvz - 啊,好的。 TextFieldParser 类可以选择引用字段,但流氓引号会破坏它:(
    • 是的“但是流氓引号打破了它”-->这就是整个问题:(
    猜你喜欢
    • 2017-08-08
    • 2013-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-23
    • 1970-01-01
    • 2019-07-05
    • 2022-11-17
    相关资源
    最近更新 更多