CSV аналізатор використовується в JQuery-CSV плагін
Це базовий аналізатор граматики Чомського типу III .
Для оцінювання даних на основі принципу «Чар-за-Чар» використовується токенізатор-регекс. Якщо виникає контрольна графіка, код передається в оператор перемикання для подальшої оцінки на основі стартового стану. Символи без контролю групуються та копіюються масово, щоб зменшити кількість необхідних операцій копіювання рядків.
Токенізатор:
var tokenizer = /("|,|\n|\r|[^",\r\n]+)/;
Перший набір збігів - це контрольні символи: роздільник значень роздільника значення (") (,) та роздільник введення (усі варіанти нового рядка). Останній збіг обробляє групування символів без контролю.
Є 10 правил, яким повинен відповідати аналізатор:
- Правило №1 - один запис на рядок, кожен рядок закінчується новим рядком
- Правило №2 - пропуск нового рядка в кінці файлу опущений
- Правило №3 - Перший рядок містить дані заголовка
- Правило №4 - Проміжки вважаються даними, а записи не повинні містити коду в кінці
- Правило № 5 - рядки можуть бути, а можуть і не бути обмежені подвійними лапками
- Правило № 6 - поля, що містять розриви рядків, подвійні лапки та коми, повинні бути укладені у подвійні лапки
- Правило № 7 - Якщо для укладання полів використовуються подвійні лапки, то подвійну цитату, що з’являється всередині поля, необхідно уникнути, передуючи їй ще однією подвійною цитатою
- Поправка №1 - поле, яке не котирується, може або може
- Поправка №2 - Поле, яке цитується, може чи не може
- Поправка №3 - Останнє поле в записі може містити або не містити нульового значення
Примітка. Найпопулярніші 7 правил отримані безпосередньо з IETF RFC 4180 . Останні 3 були додані, щоб охопити крайові регістри, запроваджені сучасними програмами електронних таблиць (Excel, Google Spreadsheet), які не розмежовують (тобто цитують) усі значення за замовчуванням. Я намагався внести зміни до RFC, але ще не почув відповіді на мій запит.
Досить з вітром, ось схема:
Штати:
- початковий стан для запису та / або значення
- зустрічається вступна цитата
- зустрічається друга цитата
- зустрічається значення без котирування
Переходи:
- а. перевіряє як цитовані значення (1), цінні без котирування (3), нульові значення (0), нульові записи (0) та нові записи (0)
- б. чеки на другу графіку (2)
- c. перевіряє наявність процитованої цитати (1), кінця значення (0) та кінця запису (0)
- г. перевіряє кінець значення (0) та кінець запису (0)
Примітка. Насправді відсутній стан. Повинно бути рядок із "c" -> "b", позначений станом "1", оскільки другий відмежувач, який уникнув, означає, що перший роздільник ще відкритий. Насправді, мабуть, було б краще представити це як інший перехід. Створювати це - мистецтво, немає єдиного правильного способу.
Примітка. У ньому також відсутній стан виходу, але на дійсних даних аналізатор завжди закінчується на переході 'a', і жоден із станів не можливий, тому що для розбору не залишається нічого.
Різниця між державами та переходами:
Стан є кінцевим, тобто можна зробити висновок лише про одне.
Перехід являє собою потік між станами, тому він може означати багато речей.
В основному, перехідний стан -> 1 -> * (тобто один на багато). Держава визначає "що це таке", а перехід визначає "як це обробляти".
Примітка. Не хвилюйтесь, якщо застосування станів / переходів не відчуває себе інтуїтивно, це не інтуїтивно. Було потрібно кілька обширних відповідей з кимось набагато розумнішим за мене, перш ніж я нарешті отримав концепцію дотримуватися.
Псевдокод:
csv = // csv input string
// init all state & data
state = 0
value = ""
entry = []
output = []
endOfValue() {
entry.push(value)
value = ""
}
endOfEntry() {
endOfValue()
output.push(entry)
entry = []
}
tokenizer = /("|,|\n|\r|[^",\r\n]+)/gm
// using the match extension of string.replace. string.exec can also be used in a similar manner
csv.replace(tokenizer, function (match) {
switch(state) {
case 0:
if(opening delimiter)
state = 1
break
if(new-line)
endOfEntry()
state = 0
break
if(un-delimited data)
value += match
state = 3
break
case 1:
if(second delimiter encountered)
state = 2
break
if(non-control char data)
value += match
state = 1
break
case 2:
if(escaped delimiter)
state = 1
break
if(separator)
endOfValue()
state = 0
break
if(newline)
endOfEntry()
state = 0
break
case 3:
if(separator)
endOfValue()
state = 0
break
if(newline)
endOfEntry()
state = 0
break
}
}
Примітка. Це суть, на практиці є ще багато, що слід врахувати. Наприклад, перевірка помилок, нульових значень, кінцевий порожній рядок (тобто, що є дійсним) тощо.
У цьому випадку стан є умовою речей, коли блок відповідності регулярних виразів закінчує ітерацію. Перехід представлений як випадок випадку.
Як люди, ми маємо тенденцію до спрощення операцій низького рівня на більш високі автореферати рівня , але робота з FSM буде працювати з операціями низького рівня. Хоча зі станами та переходами дуже легко працювати з окремими, по суті важко візуалізувати ціле все одночасно. Мені було найпростіше дотримуватися окремих шляхів виконання знову і знову, поки я не зміг зрозуміти, як відбувається перехід. Це король, як вивчити основну математику, ви не зможете оцінити код з більш високого рівня, поки деталі з низьким рівнем не стануть автоматичними.
Убік: Якщо ви подивитесь на фактичну реалізацію, то багато деталей не вистачає. По-перше, всі неможливі шляхи будуть кидати конкретні винятки. Ударити їх не можна, але якщо щось порушиться, вони абсолютно спричинить винятки в тестовому бігуні. По-друге, правила аналізатора того, що дозволено в "легальному" рядку даних CSV, досить вільні, тому код необхідний для обробки багатьох конкретних крайових випадків. Незалежно від цього факту, це був процес, який використовується для знущання над FSM перед усіма виправленнями помилок, розширеннями та тонкою настройкою.
Як і у більшості конструкцій, це не точно представлення реалізації, але воно окреслює важливі частини. На практиці є фактично 3 різні функції парсера, отримані від цієї конструкції: сплітковий лінійний спліттер, однорядний аналізатор і повний багаторядковий аналізатор. Всі вони діють аналогічно, вони відрізняються тим, як обробляють нові рядки.