Verdeel'n string ignoreer aangehaal afdelings
-
08-06-2019 - |
Vra
Gegewe'n string soos hierdie:
'n,"string, met",verskeie,"waardes, en'n paar",aangehaal
Wat is'n goeie algoritme te verdeel hierdie gebaseer op kommas terwyl ignoreer die kommas in die aangehaalde gedeeltes?
Die uitset moet word om'n verskeidenheid:
[ "'n", "string, met", "verskillende", "waardes, en'n paar", "aangehaal" ]
Oplossing
As my taal van keuse nie 'n manier om dit te doen sonder om te dink, dan sou ek aanvanklik oorweeg twee opsies as die maklike uitweg kon aanbied:
-
Pre-parse en die kommas binne die string met 'n ander beheer karakter vervang dan verdeel hulle, gevolg deur 'n post-ontleed op die skikking na die beheer karakter wat voorheen gebruik is met die kommas vervang.
-
Alternatiewelik verdeel hulle op die kommas dan post-ontleed die gevolglike skikking in 'n ander verskeidenheid nagaan vir die leiding van aanhalings op elke verskeidenheid inskrywing en concatenating die inskrywings totdat ek 'n tydelike quote bereik.
Dit is hacks egter en as dit 'n suiwer 'n geestelike "oefening dan vermoed ek hulle sal help nie bewys. As dit is 'n werklike wêreld probleem dan is dit sal help om die taal ken, sodat ons 'n paar spesifieke advies kan bied.
Ander wenke
Dit lyk of jy 'n paar goeie antwoorde hier het.
Vir dié van julle op soek na jou eie CSV parsing hanteer, ag slaan op die raad van die kenners en Moenie rol jou eie CSV parser .
Jou eerste gedagte is, "Ek moet kommas hanteer binnekant van aanhalings."
Jou volgende gedagte sal wees, "O, kak, ek moet aanhalings hanteer binnekant van aanhalings. Ontsnap aanhalings. Dubbele aanhalingstekens. Enkelaanhalingstekens ..."
Dit is 'n pad tot waansin. Moenie skryf jou eie. Vind 'n biblioteek met 'n uitgebreide eenheid toets dekking wat al die harde dele treffers en het gegaan deur die hel vir jou. Vir NET, gebruik die gratis FileHelpers biblioteek.
Python:
import csv
reader = csv.reader(open("some.csv"))
for row in reader:
print row
Natuurlik met behulp van 'n CSV parser is beter, maar net vir die pret daarvan jy kan:
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
Die skrywer hier in 'n blob van C # kode wat die scenario wat jy met 'n probleem met handvatsels gedaal:
Moet nie te moeilik om te vertaal.
Wat gebeur as 'n onewe aantal aanhalings verskyn in die oorspronklike string?
Dit lyk ongewone soos CSV parsing, wat 'n paar eienaardighede te hanteer aangehaal velde het. Die veld is net ontsnap as die veld is afgebaken met 'n dubbele aanhalings, so:
veld1, "veld2, veld3", veld4, "field5, field6" field7
raak
veld1
veld2, veld3
veld4
"field5
field6 "field7
Let as dit nie beide begin en eindig met 'n kwotasie, dan is dit nie 'n aangehaal veld en die dubbele aanhalingstekens is eenvoudig beskou as dubbele aanhalingstekens.
Insedently my kode wat iemand wat verband hou met nie eintlik hierdie reg te hanteer, as ek reg onthou.
Hier is 'n eenvoudige luislang implementering gebaseer op pseudokode Pat se:
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
Ek gebruik dit om stringe te ontleed, nie seker of dit hier help; maar met 'n paar klein veranderinge miskien?
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)
/ LP
Dit is 'n standaard CSV-styl parse. Baie mense probeer om dit te doen met gereelde uitdrukkings. Jy kan tot sowat 90% te kry met regexes, maar jy regtig nodig het 'n ware CSV parser om dit behoorlik te doen. Ek het gevind dat 'n vinnig, 'n uitstekende C # CSV parser op CodeProject 'n paar maande gelede wat Ek raai!
Hier is een in pseudokode (ook bekend as Python) in 'n keer :-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'])
Hier is 'n eenvoudige algoritme:
- bepaal of die string begin met 'n
'"'
karakter - Verdeel die string in 'n skikking afgebaken deur die
'"'
karakter. - Merk die gekwoteer kommas met 'n plekhouer
#COMMA#
- As die insette begin met 'n
'"'
, merk die items in die skikking waar die indeks% 2 == 0 - Anders merk die items in die skikking waar die indeks% 2 == 1
- As die insette begin met 'n
- koppel die items in die skikking na 'n gemodifiseerde insette string te vorm.
- Verdeel die string in 'n skikking afgebaken deur die
','
karakter. - Vervang alle gevalle in die skikking van
#COMMA#
plekhouers met die','
karakter. - Die skikking is jou uitset.
Hier is die luislang implementering:
(Vaste om te hanteer "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']
Ek kon net nie die versoeking weerstaan om te sien as ek kon maak dit werk in'n Python een-liner:
arr = [i.replace("|", ",") for i in re.sub('"([^"]*)\,([^"]*)"',"\g<1>|\g<2>", str_to_test).split(",")]
Opbrengste ["a", 'string, met', 'verskeie', 'waardes, en'n paar', 'aangehaal']
Dit werk deur die eerste vervanging van die", " binne aanhalings na'n ander skeiding (|), die verdeling van die string op ',' en die vervanging van die | separator weer.
Omdat jy sê taal agnostikus, het ek my algoritme in die taal wat die naaste aan pseudokode as moontlik:
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