拆分字符串忽略引用的部分

时间:2008-08-08 18:04:04

标签: language-agnostic parsing csv

给出这样的字符串:

  

a,“string,with”,各种各样,“值和一些”,引用

在忽略引用部分中的逗号的同时,基于逗号拆分此算法的好算法是什么?

输出应该是一个数组:

  

[“a”,“string,with”,“various”,“values and some”,“quoted”]

13 个答案:

答案 0 :(得分:20)

看起来你在这里得到了一些好的答案。

对于那些希望处理您自己的CSV文件解析的人,请听取专家和Don't roll your own CSV parser的建议。

你的第一个想法是,“我需要在引号内处理逗号。”

你的下一个想法是,“哦,废话,我需要处理引号内的引号。转义引号。双引号。单引号......”

这是通向疯狂的道路。不要自己写。找到一个具有广泛的单元测试覆盖率的图书馆,该图书馆覆盖了所有的硬件,并为您经历了地狱。对于.NET,请使用免费的FileHelpers库。

答案 1 :(得分:6)

<强>的Python:

import csv
reader = csv.reader(open("some.csv"))
for row in reader:
    print row

答案 2 :(得分:2)

当然使用CSV解析器更好,但只是为了它的乐趣:

Loop on the string letter by letter.
    If current_letter == quote : 
        toggle inside_quote variable.
    Else if (current_letter ==comma and not inside_quote) : 
        push current_word into array and clear current_word.
    Else 
        append the current_letter to current_word
When the loop is done push the current_word into array 

答案 3 :(得分:1)

这里的作者放弃了一大堆C#代码来处理你遇到问题的场景:

CSV File Imports in .Net

翻译不应太难。

答案 4 :(得分:1)

如果我选择的语言在没有考虑的情况下没有提供这样做的方法,那么我最初会考虑两种选择作为简单的出路:

  1. 使用另一个控制字符预先解析并替换字符串中的逗号,然后将它们拆分,然后对数组进行后解析,将之前使用的控制字符替换为逗号。

  2. 或者在逗号上拆分它们,然后将生成的数组解析为另一个数组,检查每个数组条目的引号并连接条目,直到我到达终止引号。

  3. 然而,这些都是黑客攻击,如果这是一种纯粹的“精神”练习,那么我怀疑它们会证明无益。如果这是一个现实世界的问题,那么了解语言将有助于我们提供一些具体的建议。

答案 5 :(得分:1)

  

如果出现奇数引号,该怎么办?   在原始字符串?

这看起来非常像CSV解析,它有一些处理引用字段的特性。如果字段用双引号分隔,则该字段仅被转义,因此:

  

field1,“field2,field3”,field4,“field5,field6”field7

变为

  

FIELD1

     

field2,field3

     

field4中

     

“字段5

     

field6“field7

请注意,如果它不是以引号开头和结尾,那么它不是引用字段,双引号只是被视为双引号。

如果我没记错的话,我的代码实际上并没有真正正确地处理这个问题。

答案 6 :(得分:1)

这是一个基于Pat的伪代码的简单python实现:

def splitIgnoringSingleQuote(string, split_char, remove_quotes=False):
    string_split = []
    current_word = ""
    inside_quote = False
    for letter in string:
      if letter == "'":
        if not remove_quotes:
           current_word += letter
        if inside_quote:
          inside_quote = False
        else:
          inside_quote = True
      elif letter == split_char and not inside_quote:
        string_split.append(current_word)
        current_word = ""
      else:
        current_word += letter
    string_split.append(current_word)
    return string_split

答案 7 :(得分:0)

我用它来解析字符串,不确定它是否有帮助;但是可能会做一些小修改?

function getstringbetween($string, $start, $end){
    $string = " ".$string;
    $ini = strpos($string,$start);
    if ($ini == 0) return "";
    $ini += strlen($start);   
    $len = strpos($string,$end,$ini) - $ini;
    return substr($string,$ini,$len);
}

$fullstring = "this is my [tag]dog[/tag]";
$parsed = getstringbetween($fullstring, "[tag]", "[/tag]");

echo $parsed; // (result = dog) 

/ MP

答案 8 :(得分:0)

这是一个简单的算法:

  1. 确定字符串是否以'"'字符开头
  2. 将字符串拆分为由'"'字符分隔的数组。
  3. 使用占位符#COMMA#标记引用的逗号
    • 如果输入以'"'开头,请在数组中标记索引%2 == 0
    • 的项目
    • 否则在数组中标记索引%2 == 1
    • 的项目
  4. 连接数组中的项以形成修改后的输入字符串。
  5. 将字符串拆分为由','字符分隔的数组。
  6. 使用#COMMA#字符替换','占位符数组中的所有实例。
  7. 数组是你的输出。
  8. 继承python的实现:
    (固定为句柄'“a,b”,c,“d,e,f,h”,“i,j,k”')

    def parse_input(input):
    
        quote_mod = int(not input.startswith('"'))
    
        input = input.split('"')
        for item in input:
            if item == '':
                input.remove(item)
        for i in range(len(input)):
            if i % 2 == quoted_mod:
                input[i] = input[i].replace(",", "#COMMA#")
    
        input = "".join(input).split(",")
        for item in input:
            if item == '':
                input.remove(item)
        for i in range(len(input)):
            input[i] = input[i].replace("#COMMA#", ",")
        return input
    
    # parse_input('a,"string, with",various,"values, and some",quoted')
    #  -> ['a,string', ' with,various,values', ' and some,quoted']
    # parse_input('"a,b",c,"d,e,f,h","i,j,k"')
    #  -> ['a,b', 'c', 'd,e,f,h', 'i,j,k']
    

答案 9 :(得分:0)

这是一种标准的CSV样式解析。很多人试图用正则表达式来做这件事。使用正则表达式可以达到约90%,但是您确实需要一个真正的CSV解析器来正确执行它。几个月前我发现fast, excellent C# CSV parser on CodeProject我强烈推荐!

答案 10 :(得分:0)

这是一遍伪代码(a.k.a.Python)中的一个:-P

def parsecsv(instr):
    i = 0
    j = 0

    outstrs = []

    # i is fixed until a match occurs, then it advances
    # up to j. j inches forward each time through:

    while i < len(instr):

        if j < len(instr) and instr[j] == '"':
            # skip the opening quote...
            j += 1
            # then iterate until we find a closing quote.
            while instr[j] != '"':
                j += 1
                if j == len(instr):
                    raise Exception("Unmatched double quote at end of input.")

        if j == len(instr) or instr[j] == ',':
            s = instr[i:j]  # get the substring we've found
            s = s.strip()    # remove extra whitespace

            # remove surrounding quotes if they're there
            if len(s) > 2 and s[0] == '"' and s[-1] == '"':
                s = s[1:-1]

            # add it to the result
            outstrs.append(s)

            # skip over the comma, move i up (to where
            # j will be at the end of the iteration)
            i = j+1

        j = j+1

    return outstrs

def testcase(instr, expected):
    outstr = parsecsv(instr)
    print outstr
    assert expected == outstr

# Doesn't handle things like '1, 2, "a, b, c" d, 2' or
# escaped quotes, but those can be added pretty easily.

testcase('a, b, "1, 2, 3", c', ['a', 'b', '1, 2, 3', 'c'])
testcase('a,b,"1, 2, 3" , c', ['a', 'b', '1, 2, 3', 'c'])

# odd number of quotes gives a "unmatched quote" exception
#testcase('a,b,"1, 2, 3" , "c', ['a', 'b', '1, 2, 3', 'c'])

答案 11 :(得分:0)

我无法抗拒地看到我是否可以在Python单行中使用它:

arr = [i.replace("|", ",") for i in re.sub('"([^"]*)\,([^"]*)"',"\g<1>|\g<2>", str_to_test).split(",")]

返回['a','字符串,带','各种','值和某些','引用']

首先将引号内部的','替换为另一个分隔符(|), 将字符串拆分为','并替换|再次分离。

答案 12 :(得分:0)

由于您说的是不可知的语言,因此我以最接近伪代码的语言编写了算法:

def find_character_indices(s, ch):
    return [i for i, ltr in enumerate(s) if ltr == ch]


def split_text_preserving_quotes(content, include_quotes=False):
    quote_indices = find_character_indices(content, '"')

    output = content[:quote_indices[0]].split()

    for i in range(1, len(quote_indices)):
        if i % 2 == 1: # end of quoted sequence
            start = quote_indices[i - 1]
            end = quote_indices[i] + 1
            output.extend([content[start:end]])

        else:
            start = quote_indices[i - 1] + 1
            end = quote_indices[i]
            split_section = content[start:end].split()
            output.extend(split_section)

        output += content[quote_indices[-1] + 1:].split()                                                                 

    return output