这是我的第一个问题。我试图找到一个答案,但老实说,不能确定我应该使用哪些条款,如果之前有人问过,那就很抱歉。
这里有: 我在.txt文件中有数千条记录,格式为:
(1, 3, 2, 1, 'John (Finances)'),
(2, 7, 2, 1, 'Mary Jane'),
(3, 7, 3, 2, 'Gerald (Janitor), Broflowski'),
......等等。第一个值是PK,其他3个是外键,第5个是字符串。
我需要在Javascript中将它们解析为JSON(或其他东西),但我遇到了麻烦,因为有些字符串有括号+逗号(在第3条记录上,“Janitor”,例如),所以我不能使用子字符串。 ..也许修剪正确的部分,但我想知道是否有更聪明的方法来解析它。
任何帮助都会非常感激。
谢谢!
答案 0 :(得分:14)
你不能(读可能不应该)使用正则表达式。如果括号包含另一对或一个不匹配怎么办?
好消息是你可以轻松地为此构建一个标记器/解析器。 我们的想法是跟踪您当前的状态并采取相应的行动。
这是我刚才写的解析器的 sketch ,重点是向您展示一般想法。如果您对此有任何概念性问题,请与我们联系。
它有效 demo here但我请你在理解和修补之前不要在制作中使用它。
那么,我们如何构建解析器:
var State = { // remember which state the parser is at.
BeforeRecord:0, // at the (
DuringInts:1, // at one of the integers
DuringString:2, // reading the name string
AfterRecord:3 // after the )
};
我们需要跟踪输出和当前工作对象,因为我们将一次解析这些对象。
var records = []; // to contain the results
var state = State.BeforeRecord;
现在,我们迭代字符串,继续进行并读取下一个字符
for(var i = 0;i < input.length; i++){
if(state === State.BeforeRecord){
// handle logic when in (
}
...
if(state === State.AfterRecord){
// handle that state
}
}
现在,剩下的就是将它消耗到每个州的对象中:
(
我们开始解析并跳过任何空格,
'
到下一个'
的字符串到达结尾)
,存储对象,然后再次开始循环。实施也不是很困难。
var State = { // keep track of the state
BeforeRecord:0,
DuringInts:1,
DuringString:2,
AfterRecord:3
};
var records = []; // to contain the results
var state = State.BeforeRecord;
var input = " (1, 3, 2, 1, 'John (Finances)'), (2, 7, 2, 1, 'Mary Jane'), (3, 7, 3, 2, 'Gerald (Janitor), Broflowski')," // sample input
var workingRecord = {}; // what we're reading into.
for(var i = 0;i < input.length; i++){
var token = input[i]; // read the current input
if(state === State.BeforeRecord){ // before reading a record
if(token === ' ') continue; // ignore whitespaces between records
if(token === '('){ state = State.DuringInts; continue; }
throw new Error("Expected ( before new record");
}
if(state === State.DuringInts){
if(token === ' ') continue; // ignore whitespace
for(var j = 0; j < 4; j++){
if(token === ' ') {token = input[++i]; j--; continue;} // ignore whitespace
var curNum = '';
while(token != ","){
if(!/[0-9]/.test(token)) throw new Error("Expected number, got " + token);
curNum += token;
token = input[++i]; // get the next token
}
workingRecord[j] = Number(curNum); // set the data on the record
token = input[++i]; // remove the comma
}
state = State.DuringString;
continue; // progress the loop
}
if(state === State.DuringString){
if(token === ' ') continue; // skip whitespace
if(token === "'"){
var str = "";
token = input[++i];
var lenGuard = 1000;
while(token !== "'"){
str+=token;
if(lenGuard-- === 0) throw new Error("Error, string length bounded by 1000");
token = input[++i];
}
workingRecord.str = str;
token = input[++i]; // remove )
state = State.AfterRecord;
continue;
}
}
if(state === State.AfterRecord){
if(token === ' ') continue; // ignore whitespace
if(token === ',') { // got the "," between records
state = State.BeforeRecord;
records.push(workingRecord);
workingRecord = {}; // new record;
continue;
}
throw new Error("Invalid token found " + token);
}
}
console.log(records); // logs [Object, Object, Object]
// each object has four numbers and a string, for example
// records[0][0] is 1, records[0][1] is 3 and so on,
// records[0].str is "John (Finances)"
答案 1 :(得分:4)
我回应Ben's sentiments关于正常表达通常对此不好的事情,并且我完全同意他,令牌化器是最好的工具。
然而,给出一些警告,你可以在这里使用正则表达式。这是因为(
,)
,,
和'
中的任何歧义都可归因于(AFAIK)到您的最后一栏;因为所有其他列总是是整数。
所以,给定:
(
,)
,,
或'
)。 ...以下内容应该有效(请注意这里的“新行”是\n
。如果它们是\r\n
,请相应更改):
var input = /* Your input */;
var output = input.split(/\n/g).map(function (cols) {
cols = cols.match(/^\((\d+), (\d+), (\d+), (\d+), '(.*)'\)/).slice(1);
return cols.slice(0, 4).map(Number).concat(cols[4]);
});
代码在新行上分割,然后逐行遍历并使用正则表达式分割成单元格,该表达式尽可能地贪婪地归因于最终单元格。然后它将前4个元素转换为整数,并将第5个元素(字符串)粘贴到末尾。
这为您提供了一系列记录,其中每条记录本身就是一个数组。前4个元素是你的PK(作为整数),你的第5个元素是字符串。
例如,根据您的输入,使用output[0][4]
获取"Gerald (Janitor), Broflowski"
,使用output[1][0]
获取第二条记录的第一个PK 2
(不要忘记JavaScript数组是零索引的。)
您可以在此处看到它:http://jsfiddle.net/56ThR/
答案 2 :(得分:3)
另一种选择是将其转换为类似于Array
和eval
的内容。我知道不建议使用eval,但这是一个很酷的解决方案:)
var lines = input.split("\n");
var output = [];
for(var v in lines){
// Remove opening (
lines[v] = lines[v].slice(1);
// Remove closing ) and what is after
lines[v] = lines[v].slice(0, lines[v].lastIndexOf(')'));
output[v] = eval("[" + lines[v] + "]");
}
因此,eval parameter
看起来像:[1, 3, 2, 1, 'John (Finances)']
,它确实是一个数组。
演示:http://jsfiddle.net/56ThR/3/
并且,它也可以写得更短:
var lines = input.split("\n");
var output = lines.map( function(el) {
return eval("[" + el.slice(1).slice(0, el.lastIndexOf(')') - 1) + "]");
});
答案 3 :(得分:1)
你可以随时做到&#34;手动&#34; :)
var lines = input.split("\n");
var output = [];
for(var v in lines){
output[v] = [];
// Remove opening (
lines[v] = lines[v].slice(1);
// Get integers
for(var i = 0; i < 4; ++i){
var pos = lines[v].indexOf(',');
output[v][i] = parseInt(lines[v].slice(0, pos));
lines[v] = lines[v].slice(pos+1);
}
// Get string betwen apostrophes
lines[v] = lines[v].slice(lines[v].indexOf("'") + 1);
output[v][4] = lines[v].slice(0, lines[v].indexOf("'"));
}
答案 4 :(得分:0)
这里你所拥有的基本上是你要解析的csv(逗号分隔值)文件。
最简单的方法是使用一个wxternal库来处理你遇到的大多数问题
示例:jquery csv库是一个很好的库。 https://code.google.com/p/jquery-csv/