Как извлечь информацию о банковских транзакциях с правильным условием регулярного выражения

#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