3

我有以下用于 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" 结果

  • “布拉布拉”
  • “布拉布拉2”
  • 3
  • 4
  • “5”
  • "bla"bla","结束"

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

一个“有效”的右引号总是后跟一个逗号 (,) 或一个控制换行符

添加 了这似乎可以解决它

// 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';
4

3 回答 3

2

尝试这样的事情:

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的引号,而 6又是字符串7的最后一个字符或后跟\r 8\n 9或引号" 10或数字11

于 2013-08-08T08:39:09.097 回答
1

如果您可以选择基于 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++ 使用的方式;

于 2013-08-08T10:47:41.280 回答
0

作为替代方案,只要您不打算,在引号内加上 a ,您就可以查看Microsoft.VisualBasic.FileIO.TextFieldParser

以下代码片段:

using Microsoft.VisualBasic.FileIO;


using (TextFieldParser parser = new TextFieldParser(fileName))
{

    parser.Delimiters = new string[] { "," };

    while (!parser.EndOfData)
    {
        string[] fields = parser.ReadFields();   
    }
}

上面的代码片段生成一个包含示例行的数组,如下所示:

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

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

于 2013-08-08T08:36:44.673 回答