#javascript #node.js #regex #ecmascript-6
#javascript #node.js #регулярное выражение #ecmascript-6
Вопрос:
Моя цель — прочитать банковский документ, который содержит все мои транзакции за месяц. Сегодня я вручную ввожу все свои транзакции в Excel. Я хотел бы автоматизировать это. Таким образом, сегодня днем я пытался прочитать PDF и извлечь текст.
Для этого я использовал библиотеку pdf-parse. В документе строки транзакций выглядят следующим образом :
31-12-2019 Forfait 01-01-2019 - 31-12-2019 -29,00 // (loose money)
31-12-2019 Forfait 01-01-2019 - 31-12-2019 50,00 // (win money)
31-12-2019 Mercedes -500,00 // (loose money)
31-12-2019 Client 10 700,00 // (win money)
Но с программой чтения PDF я получаю :
31-12-2019Forfait 01-01-2019 - 31-12-2019-29,00
31-12-2019Forfait 01-01-2019 - 31-12-201950,00
31-12-2019Mercedes-500,00
31-12-2019Client10 700,00
Моя цель — извлечь: дата — текст — сумма.
const transactions = data.text.split('n').filter(val => {
// regex matching the transaction line of my pdf
});
Я хотел бы извлечь это с помощью некоторого регулярного выражения.
Но я не могу найти правильный путь. Для потери денег мы могли бы легко ее разделить.
Однако, когда транзакция положительная, становится трудно определить правильную сумму, например :
31-12-2019Forfait 01-01-2019 - 31-12-201950,00
// expected { date:31-12-2019 text: Forfait 01-01-2019 - 31-12-2019 amount: 50,00 }
31-12-2019Client10 700,00
// expected { date:31-12-2019 text: Client amount: 10 700,00 }
Я пытался :
(d{2}-d{2}-d{4})([a-zA-Z!@#%$amp;'()-`." ] )([-?d ]*,[d] )
Но не охватывает все случаи, которые я нашел.
Обычно, когда в описании у вас есть номер в конце.
другой пример :
31-12-2019Comptoir1750,00
// expected 31-12-2019 Comptoir17 50,00
У вас есть какие-либо идеи?
большое спасибо,
Комментарии:
1. Предлагает ли ваш банк ваши транзакции в каком-либо другом формате, кроме PDF? Многие банки также предлагают их в формате CSV. Однако JSON, XML и другие форматы будут намного проще для анализа, чем PDF. Или есть причина, по которой вы должны использовать именно PDF?
2. Как вы можете отличить эти 2 друг от друга?
Comptoir17
и50,00
?3. @Thefourthbird действительно, это именно моя проблема. Но сегодня я нашел другую программу чтения pdf, которая дает мне лучшее разделение текстовых данных в массиве. И я могу применить определенный шаблон к этому результату массива.
4. @3limin4t0r Хороший вопрос, эти документы являются официальными. Я должен подключиться к панели управления моего онлайн-банка, чтобы узнать, есть ли другой тип документа. : p
5. Если нет шаблонов, которые можно назвать, чтобы различать все возможные случаи, то также нет способа поместить это в код, особенно с
Comptoir 1750,00
которым может быть что-то вродеComptoir 1 750,00
илиComptoir 17 50,00
. Но17-12-19 to 22-12-1950,00
имеет шаблон, который я бы разрешил17-12-19 to 22-12-19 50,00
из-за повторяющегося шаблона датыDD-MM-YY
. На данный момент более чистый подход снова сосредоточился на получении полностью надежных входных данных (процесс, который не разрушает семантику исходного документа, в отличие от текущего pdf-reader).
Ответ №1:
Регулярное выражение с именованными группами захвата, такими как … (/^s*(?<date>d{2}-d{2}-d{4})s*(?<text>[^s-] (?:(?:[s-]*d{2}-d{2}-d{4}) )?)s*(?<amount>-*d ,d )/gm)
… включает подход, основанный на … data.text.
matchAll
…
const data = { text: `31-12-2019Forfait 01-01-2019 - 31-12-2019-29,00
31-12-2019Forfait 01-01-2019 - 31-12-201950,00
31-12-2019Mercedes-500,00
31-12-2019Client10 700,00
31-12-2019Comptoir17 50,00` };
// see: [https://regex101.com/r/7TdghZ/1]
// const regXDataCaptures = (/^(?<date>[d-] )s*(?<text>[^s-] (?:(?:[s-]*d{2}-d{2}-d{4}) )?)s*(?<amount>-*d ,d )/gm);
const regXDataCaptures = (/^s*(?<date>d{2}-d{2}-d{4})s*(?<text>[^s-] (?:(?:[s-]*d{2}-d{2}-d{4}) )?)s*(?<amount>-*d ,d )/gm);
const dataList = [
...data.text.matchAll(regXDataCaptures)
].map(({ groups }) => ({ ...groups }));
console.log('dataList :', dataList);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Чтобы избежать слишком сложных выражений (с точки зрения понимания и обслуживания), а также быть менее уязвимым для крайних случаев, я лично предпочитаю разбивать такие процессы на подзадачи, которые могут более надежно обрабатывать каждую проблему за раз.
Решение, которое следует такому подходу, может использовать два регулярных выражения, например … (/^s*(?<date>d{2}-d{2}-d{4})/)
и (/(?:(?:[s-]*d{2}-d{2}-d{4}) )?s*(?<amount>-*d ,d )s*$/)
… и может выглядеть аналогично следующему предоставленному…
const data = { text: `31-12-2019Forfait 01-01-2019 - 31-12-2019-29,00
31-12-2019Forfait 01-01-2019 - 31-12-201950,00
31-12-2019Mercedes-500,00
31-12-2019 Client 10 700,00
31-12-2019Comptoir 17-50,00` };
// see: [https://regex101.com/r/Oj11JY/1/]
const regXCaptureDate = (/^s*(?<date>d{2}-d{2}-d{4})/);
// see: [https://regex101.com/r/LTgHd2/1/]
const regXCaptureAmount = (/(?:(?:[s-]*d{2}-d{2}-d{4}) )?s*(?<amount>-*d ,d )s*$/);
function isNonEmptyStringValue(value) {
return ((typeof value === 'string') amp;amp; (value !== ''));
}
function createAndCollectRecord(list, rawRecordItem) {
const record = {
...(regXCaptureDate.exec(rawRecordItem) || {}).groups,
...(regXCaptureAmount.exec(rawRecordItem) || {}).groups,
text: rawRecordItem
.replace(regXCaptureDate, '')
.replace(regXCaptureAmount, '')
.trim()
};
if (
['date', 'amount', 'text']
.every(key => isNonEmptyStringValue(record[key]))
) {
list.push(record);
}
return list;
}
const dataList = data.text
.split(/n/)
.reduce(createAndCollectRecord, []);
console.log('dataList :', dataList);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Комментарии:
1. Привет, спасибо за ваш отзыв. Моя проблема была с такой строкой: 31-12-2019Comptoir 1750,00. Я не могу различить цену: это 1750,00 или 50,00 или 750,00? То же самое я получаю: 31-12-2019Comptoir с 17-12-19 по 22-12-1950,00.
Ответ №2:
Это должно сработать, оно охватывает все предоставленные вами случаи. Хотя я предостерегаю от этого подхода, поскольку он хрупкий и может легко сломаться в случаях, с которыми вы еще не сталкивались в своих данных.
https://regex101.com/r/tMbNPZ/1
^(d{1,2}-d{1,2}-d{2,4})(([wd] s?d{2}-d{2}-d{4}]?s-sd{2}-d{2}-d{4})|([w] ))(([d,] )|(s[d,] )|(-[d,] ))
Комментарии:
1. Эй, возможно, я плохо выразился. Я имел в виду, что я уже извлекаю эти строки. Проблема конкретно в том, что у нас есть: 31-12-2019Comptoir 1750,00. Я не могу различить цену: это 1750,00 или 50,00 или 750,00? То же самое, если я получу: 31-12-2019Comptoir с 17-12-19 по 22-12-1950,00