Регулярное выражение стихов из Священного Писания

Я пытался создать собственное регулярное выражение для стихов из Священного Писания, которое могло бы вместить широкий спектр типов Священного Писания. Я скопировал часть этого регулярного выражения из другого вопроса о stackoverflow. Проблема, с которой я столкнулся, заключается в том, что он не находит стих после запятой и после диапазона стихов, как в Ос 10:1-3, 8. Он находит Ос 10:1-3, но не 8. Проблема сводится к \S, который у меня там есть, но он мне нужен, чтобы регулярное выражение находило отрывки из Священных Писаний, которые находятся рядом друг с другом, где второй отрывок начинается с числа, например 1 Петра. Регулярное выражение:

((?:(I+|1st|2nd|3rd|First|Second|Third|[123])\s)?(Gen|Ge|Gn|Exo|Ex|Exod|Lev|Le|Lv|Num|Nu|Nm|Nb|Deut|Dt|Josh|Jos|Jsh|Judg|Jdg|Jg|Jdgs|Rth|Ru|Sam|Samuel|Kings|Kgs|Kin|Chron|Chronicles|Ezra|Ezr|Ez|Neh|Ne|Esth|Es|Job|Job|Jb|Pslm|Ps|Psalms|Psa|Psm|Pss|Prov|Pr|Prv|Eccles|Ec|Song|So|Canticles|Song of Songs|SOS|Isa|Is|Jer|Je|Jr|Lam|La|Ezek|Eze|Ezk|Dan|Da|Dn|Hos|Ho|Joel|Joe|Jl|Amos|Am|Obad|Ob|Jnh|Jon|Micah|Mic|Nah|Na|Hab|Zeph|Zep|Zp|Haggai|Hag|Hg|Zech|Zec|Zc|Mal|Mal|Ml|Matt|Mt|Mrk|Mk|Mr|Luk|Lk|John|Jn|Jhn|Acts|Ac|Rom|Ro|Rm|Co|Cor|Corinthians|Gal|Ga|Ephes|Eph|Phil|Php|Col|Col|Th|Thes|Thess|Thessalonians|Ti|Tim|Timothy|Titus|Tit|Philem|Phm|Hebrews|Heb|He|James|Jas|Jm|Pe|Pet|Pt|Peter|Jn|Jo|Joh|Jhn|John|Jude|Jd|Jud|Jud|Rev|The Revelation|Genesis|Exodus|Leviticus|Numbers|Deuteronomy|Joshua|Judges|Ruth|Samuel|Kings|Chronicles|Ezra|Nehemiah|Esther|Job|Psalms|Psalm|Proverbs|Ecclesiastes|Song of Solomon|Isaiah|Jeremiah|Lamentations|Ezekiel|Daniel|Hosea|Joel|Amos|Obadiah|Jonah|Micah|Nahum|Habakkuk|Zephaniah|Haggai|Zechariah|Malachi|Matthew|Mark|Luke|John|Acts|Romans|Corinthians|Galatians|Ephesians|Philippians|Colossians|Thessalonians|Timothy|Titus|Philemon|Hebrews|James|Peter|John|Revelation|Re|Ap))(.?\s?\d+((?:[:.]\d+)?(\s?[-–—]\s?)?(?:\d+)(?:(,\s?\d+)*)?\S([:.]?\d+)?(,?\s?\d+[–—-]\s?\d+,?\d+)?)?(?:[:.]\d+)?(?:[abcde])?(?:,\d+)*(?:[-–—]\d?\s?)?)(?:[:.]\d+[–-—]\s?\d+,?\s?\d+)?

У меня есть ссылка на тестер со многими типами отрывков, которые я хочу найти: ссылка на тестер регулярных выражений с примерами Есть ли у кого-нибудь предложения о том, как отредактировать это, чтобы найти Ос 10:1-3, 8? Я новичок в регулярном выражении, поэтому подозреваю, что это должно быть простым решением для более опытного регулярного выражения. Я пробовал довольно много разных комбинаций, но безуспешно, поскольку каждая новая комбинация, которую я пробовал, прерывает поиск текущих отрывков, которые я уже смог протестировать.

Как вы построили это регулярное выражение? Его очень сложно читать, и в нем уже есть ошибки (например, могут совпадать II Gen 28:30 и DeutX 28:30). Вероятно, вам повезет больше, написав это снизу вверх.

brandizzi 12.06.2024 21:34

Не зная полного набора шаблонов, которые вы пытаетесь сопоставить, я не понимаю, чем мы можем помочь, потому что любое сделанное предложение может, например, сломать что-то, что в настоящее время работает.

A Haworth 12.06.2024 23:08

Я дал ссылку на свой тестер регулярных выражений regex101.com/r/TqfbeW/1

Jeremy Menicucci 13.06.2024 04:11

Если вы хотите сопоставить Hos 10:1-3, 8, вы также должны хотеть сопоставить Genesis 2:1 - 3:19, 1, но кажется, что с Genesis 2:1 - 3:19 у вас все в порядке — это противоречиво. Мы не сможем помочь, пока вы не объясните правила.

Wiktor Stribiżew 13.06.2024 09:07

Возьмем простой пример: как нам разобрать Лк 1, 2 Петра. Можем ли мы с уверенностью предположить, что Петру всегда предшествует цифра 1 или 2 (и аналогично для всех имен с несколькими книгами). т.е. является ли привязка цифры перед определенными, известными названиями книг сильнее, чем любая цифра, возможно, принадлежащая предыдущей ссылке?

A Haworth 13.06.2024 09:17

Учитывая, что здесь существует какая-то иерархия/приоритет [вы должны знать, что Первое послание Петра - это книга и что 1 не может принадлежать предыдущей ссылке], я бы предложил закодировать это самостоятельно, например. в JavaScript, а не пытаться заставить регулярное выражение сделать что-то, в чем оно не очень хорошо справляется. JS приемлем?

A Haworth 13.06.2024 14:45

@WiktorStribiżew Нет, я не хочу сопоставлять Бытие 2:1 - 3:19, 1, потому что последняя 1 будет либо книгой, либо номером ссылки на следующий отрывок, либо это не будет иметь смысла, поскольку глава варьируется для Бытия. являются главами со 2 по 3, ни один ресурс никогда не будет перечислять главу 1 после глав со 2 по 3.

Jeremy Menicucci 13.06.2024 14:48

@AHaworth Да, мы предполагаем, что перед Питером всегда будет 1 или 2. Я определенно готов написать его на JS, а не использовать регулярное выражение. Я думал, что регулярное выражение будет быстрее, но для некоторых более сложных случаев прохода оно оказывается очень сложным.

Jeremy Menicucci 13.06.2024 14:50

Но мы не можем предполагать, что Джону всегда будет предшествовать 1 или 2, поэтому я не понимаю, как может существовать полностью автоматический способ анализа вашего синтаксиса.

A Haworth 13.06.2024 15:30

@AHaworth Это правда, но обычно у меня не возникало проблем с сопоставлением книг с числовыми префиксами в строке, но при попытке найти такие ситуации, как Осия 2:1-3, 10, я в конечном итоге фиксировал Иоанна 3:16-17, 1, когда за ним последовало 1 Петра 1:1, что в конечном итоге подтверждает вашу точку зрения. Я полагаю, что невозможно иметь регулярное выражение, охватывающее все эти ситуации. Похоже, нужно реализовать больше JS.

Jeremy Menicucci 13.06.2024 15:57

Что бы вы ни использовали, вам придется столкнуться с фактом наличия аномалии. Возможно, просто сообщите пользователю, что есть несколько Джонов, и потом попросите их починить вещи.

A Haworth 13.06.2024 16:00
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
11
127
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я не буду комментировать ту часть регулярного выражения, которая соответствует названию или аббревиатуре книги, а сосредоточусь на второй части, которая соответствует главе(ам) и стиху(ам):

  • Он начинается с (.?, но похоже, что вы хотите сопоставить здесь буквальную точку, поэтому ее следует экранировать: (\.?

  • У вас есть конструкции, которые могут привести к экспоненциальному возврату, как показано в этом фрагменте: \d+)?(\s?[-–—]\s?)?(?:\d+. Если эта средняя необязательная группа не захватывает никаких символов, у вас есть \d+, за которым следует \d+, а это означает, что если произойдет возврат к этой части регулярного выражения, распределение цифр изменится на все возможные разделения. Этого следует избегать.

  • Символ \S не может быть правильным: он соответствует любому символу, не являющемуся пробелом, который может быть знаком пунктуации, буквой... что, безусловно, будет неправильным здесь.

  • Существует много ненужных повторений: иногда у вас есть поддержка определенного шаблона, который не поддерживается где-либо еще, например, суффикс [abcde]. Это кажется произвольным. По возможности лучше избегать такого дублирования.

Теперь к сути вопроса: как избежать того, чтобы цифра перед названием книги фиксировалась так, как будто это глава/стих, принадлежащий предыдущей ссылке?

Для этого вы можете использовать просмотр вперед. Например, перед сопоставлением пробела, за которым следует цифра, в качестве главы/стиха вы можете утверждать, что за этими двумя символами не следует пробел и заглавная буква, поскольку это будет указывать на то, что эта цифра на самом деле является префиксом номера книги.

Вот предлагаемое исправление этой второй части вашего регулярного выражения:

\.?\s(?:\d+[:.])?\d+[a-e]?(?:(?:\s?[-–—]|,)(?!\s[1-3]\s+[A-Z])\s?(?:\d+[:.])?\d+[a-e]?)*

Однако обратите внимание, что есть такие неясности:

Иуды 1, 2 Иоанна 1:5

...что может означать «(Иуды 1), (2 Иоанна 1:5)» или «(Иуды 1, 2) (Иоанна 1:5)». Предлагаемое регулярное выражение будет использовать первый вариант. Я предполагаю, что во втором случае вам лучше использовать знаки препинания или текст между двумя ссылками, например:

Иуды 1, 2; Иоанна 1:5

или так:

Иуды 1, 2 и Иоанна 1:5.

Ответ принят как подходящий

Есть несколько проблем с подходом с использованием регулярных выражений.

Во-первых, названия книг могут иметь форму 2 Петра, и это можно спутать с введенной формой, например: Быт 1, 2 Петра.

Для большинства книг, которые могут иметь цифры в начале, эту проблему можно обойти, добавив эти версии в список названий книг и явно выполнив их поиск, прежде чем делать что-либо еще.

Однако это не может решить проблему Джона, который может иметь предшествующую цифру или нет. Никакой умный анализ не может решить эту проблему, и единственный человек, который может ее решить, — это тот, кто является автором текста и знает, что он означает.

Существует также тот факт, что . может использоваться в конце названия книги, хотя это также можно сделать, добавив такие строки в список названий книг перед их поиском.

Основная проблема (IMO) — сложность понимания регулярных выражений, которые имеют дело с этими особыми случаями. Для удобства сопровождения я бы предложил кодировать менее сложным способом.

Итак, я отказался от регулярных выражений из-за проблем и вместо этого вот версия JS - очень неотшлифованная и немного хакерская, но она справляется с проблемами 2 Петра и т. д.

Авторам придется проверить себя на наличие проблемы Джона, но она вряд ли будет возникать часто, поскольку числа после предыдущего должны быть небольшими. Такая человеческая проверка потребуется, каким бы сложным и всеобъемлющим ни был код/регулярное выражение.

<style>
  span {
    background: pink;
  }
</style>
<div></div>
<script>
  const books = ['Gen', 'Ge', 'Gn', 'Exo', 'Ex', 'Exod', 'Lev', 'Le', 'Lv', 'Num', 'Nu', 'Nm', 'Nb', 'Deut', 'Dt', 'Josh', 'Jos', 'Jsh', 'Judg', 'Jdg', 'Jg', 'Jdgs', 'Rth', 'Ru', 'Sam', 'Samuel', 'Kings', 'Kgs', 'Kin', 'Chron', 'Chronicles', 'Ezra', 'Ezr', 'Ez', 'Neh', 'Ne', 'Esth', 'Es', 'Job', 'Job', 'Jb', 'Pslm', 'Ps', 'Psalms', 'Psa', 'Psm', 'Pss', 'Prov', 'Pr', 'Prv', 'Eccles', 'Ec', 'Song', 'So', 'Canticles', 'Song of Songs', 'SOS', 'Isa', 'Is', 'Jer', 'Je', 'Jr', 'Lam', 'La', 'Ezek', 'Eze', 'Ezk', 'Dan', 'Da', 'Dn', 'Hos', 'Ho', 'Joel', 'Joe', 'Jl', 'Amos', 'Am', 'Obad', 'Ob', 'Jnh', 'Jon', 'Micah', 'Mic', 'Nah', 'Na', 'Hab', 'Zeph', 'Zep', 'Zp', 'Haggai', 'Hag', 'Hg', 'Zech', 'Zec', 'Zc', 'Mal', 'Mal', 'Ml', 'Matt', 'Mt', 'Mrk', 'Mk', 'Mr', 'Luk', 'Lk', 'John', 'Jn', 'Jhn', 'Acts', 'Ac', 'Rom', 'Ro', 'Rm', 'Co', 'Cor', 'Corinthians', 'Gal', 'Ga', 'Ephes', 'Eph', 'Phil', 'Php', 'Col', 'Col', 'Th', 'Thes', 'Thess', 'Thessalonians', 'Ti', 'Tim', 'Timothy', 'Titus', 'Tit', 'Philem', 'Phm', 'Hebrews', 'Heb', 'He', 'James', 'Jas', 'Jm', 'Pe', 'Pet', 'Pt', 'Peter', 'Jn', 'Jo', 'Joh', 'Jhn', 'John', 'Jude', 'Jd', 'Jud', 'Jud', 'Rev', 'The Revelation', 'Genesis', 'Exodus', 'Leviticus', 'Numbers', 'Deuteronomy', 'Joshua', 'Judges', 'Ruth', 'Samuel', 'Kings', 'Chronicles', 'Ezra', 'Nehemiah', 'Esther', 'Job', 'Psalms', 'Psalm', 'Proverbs', 'Ecclesiastes', 'Song of Solomon', 'Isaiah', 'Jeremiah', 'Lamentations', 'Ezekiel', 'Daniel', 'Hosea', 'Joel', 'Amos', 'Obadiah', 'Jonah', 'Micah', 'Nahum', 'Habakkuk', 'Zephaniah', 'Haggai', 'Zechariah', 'Malachi', 'Matthew', 'Mark', 'Luke', 'John', 'Acts', 'Romans', 'Corinthians', 'Galatians', 'Ephesians', 'Philippians', 'Colossians', 'Thessalonians', 'Timothy', 'Titus', 'Philemon', 'Hebrews', 'James', 'Peter', 'John', 'Revelation', 'Re', 'Ap', 'Jd.', 'Heb.'];

  const preStrings = ['III', 'II', 'I', '1st', '2nd', '3rd', 'First', 'Second', 'Third', '1', '2', '3'];
  const preStringed = ['Sam', 'Samuel', 'Kings', 'Kgs', 'Kin', 'Chron', 'Chronicles', 'Corinthians', 'Co', 'Cor', 'Thessalonians', 'Th', 'Thes', 'Thess', 'Timothy', 'Ti', 'Tim', 'Peter', 'Pe', 'Pet', 'Pt', 'John', 'Jn', 'Jhn'];
  let text = `Joel 10:13 The passages Luke 2:32 and Lk 1:23 that we are looking at tonight 1 Cor 12:34 2 Cor 3:4 are found Jude 6, in Jude 5, Genesis 2:1 - 3:19, 1 John 3:16-17, 1 Peter 1:1, and Romans 10:13, 15, 17. Please turn in your Bibles. Ps 109:4,5,6,8.  Isaiah 61.2-3 Mt 5.4

Ge 27.27-29,89-40 Heb 11.20 Heb. 12.17 Jonah 3

Jd. 5
Jd 6

1 Cor 12:34 2 Cor 3:4. He 4.12 Re 1.16

Leviticus 16:6 He 5.3 He 7.27

Hos 10:1-3, 8 and 1 John 2:23`;
  //add the prestringed versions e.g. 1 Peter
  for (let b = 0; b < preStringed.length; b++) {
    for (let pre = 0; pre < preStrings.length; pre++) {
      books.push(preStrings[pre] + ' ' + preStringed[b]);
    }
  }
  // add the book name with . at the end as this seems to be added sometimes, at least to the shortened forms
  const length = books.length;
  for (let b = 0; b < length; b++) {
    books.push(books[b] + '.');
  }

  // sort descending - longer items first
  books.sort((a, b) => b.length - a.length);
  let booksAt = [];
  // go thro' each book finding where it matches in text
  for (let b = 0; b < books.length; b++) {
    const book = books[b];
    let chNoInText = 0;
    while (chNoInText < text.length) {
      let j = text.indexOf(book, chNoInText);
      if (j < 0) break;
      if (((j + book.length) < text.length) && !(text.charAt(j + book.length).match(/^[a-z]+$/))) {
        booksAt.push([book, j]);
        let replacement = book;
        for (let k = 0; k < book.length; k++) {
          replacement = replacement.replace(book.charAt(k), 'X');
        }
        text = text.replace(book, replacement); // to prevent a shorter version matching
      }
      chNoInText = j + book.length + 1;
    }
  }
  // into ascending order of start position
  booksAt.sort(function(a, b) {
    return a[1] - b[1];
  });
  newText = '';
  let chNoInText = 0;
  for (let b = 0; b < booksAt.length; b++) {
    while (chNoInText < booksAt[b][1]) { //copy across characters to start of book
      newText += text.charAt(chNoInText);
      chNoInText++;
    }
    newText += '<span>' + booksAt[b][0] + '</span><span>';
    chNoInText += booksAt[b][0].length; //skip the 'fill-in characters
    for (let i = 0; i < 100; i++) {
      chNoInText++;
      const nextCh = text.charAt(chNoInText);
      //test whether are at the end of the chapter(s) and verse(s)
      if (nextCh.match(/^[a-z]+$/)) break;
      if (nextCh.match(/^[A-Z]+$/)) break;
      newText += text.charAt(chNoInText - 1);
    };
    newText += '</span>&nbsp;';
  }
  document.querySelector('div').innerHTML = newText;
</script>

Мне это кажется довольно удивительным, я подключу это и начну с этого. Большое спасибо!!

Jeremy Menicucci 15.06.2024 16:26

Да, это выглядит как отличное решение. Еще раз спасибо!

Jeremy Menicucci 15.06.2024 16:31

При возникновении проблемы с этим, как упомянуто в моем комментарии ниже, у этого решения возникают проблемы при поиске как Бытия 1:1, так и Иоанна 1:1. В настоящее время находится Бытие 1 и 1 Иоанна 1:1. В этом случае необходимо будет найти Иоанна 1:1, поскольку перед первым посланием Иоанна не будет двоеточия. Есть идеи, как быстро это исправить?

Jeremy Menicucci 16.07.2024 04:49

Мне удалось составить регулярное выражение, соответствующее всем примерам в вашей ссылке regex101: https://regex101.com/r/J2Tpwz/2 - если у вас есть более полный список книг, к которым будет добавлен префикс, вы можете добавить их в раздел, который сейчас выглядит так John|Peter|Cor

Ответ @a-haworth кажется очень полным и, скорее всего, будет работать для большой группы входных текстов. Просто добавьте это, если вам нужно что-то чисто на основе регулярных выражений

Регулярное выражение, показанное по этой ссылке: /(?:\b\d+ )?[A-Z]\w+\.? [\d: .\-]+(?:, ?[\d\-]+)*(?<![ .])(?! (?:John|Peter|Cor))/g

и разбивка такая:

  • (?:\b\d+ )? — опционально соответствует начальному числу
  • [A-Z] – убедитесь, что название книги начинается с заглавной буквы.
  • \w+\.? [\d: .\-]+ — серия словесных символов, за которыми следует необязательная точка, пробел, а затем последовательность цифр, двоеточий, точек и дефисов.
  • (?:, ?[\d\-]+)* — необязательная дополнительная повторяющаяся последовательность запятой, необязательный пробел, а затем последовательность цифр и дефисов.
  • (?<![ .]) — отрицательный просмотр, чтобы убедиться, что конечная группа не содержит завершающего пробела или точки.
  • (?! (?:John|Peter|Cor)) — отрицательный просмотр вперед, чтобы гарантировать, что за окончательным совпадением числа не сразу следует название книги.

Я нашел это регулярное выражение весьма полезным. Однако я заметил одну проблему: когда два отрывка разделены одним пробелом, например Бытие 1:1 Иоанна 1:1, это отражает Бытие 1 и 1 Иоанна 1:1. В данном случае это будет Иоанна 1:1, поскольку Первое послание Иоанна не начинается с двоеточия перед ним. Итак, мне интересно, как мы можем добавить, может быть, негативный взгляд позади (?<!:) или что-то в этом роде, чтобы проверить, прикреплено ли оно к двоеточию в предыдущей ссылке. Имеет ли это смысл?

Jeremy Menicucci 16.07.2024 04:42

Рад слышать, что это было полезно - в конце можно добавить дополнительное условие, которое проверяет, применяется ли отрицательный просмотр только в том случае, если ему непосредственно не предшествует двоеточие, за которым следуют цифры и, возможно, дефисы. ОДНАКО ошибка regex101 в этом случае, потому что ему не нравится квантификатор внутри просмотра назад, поэтому независимо от того, в чем вы это запускаете, это тоже может задохнуться. Вот обновленный шаблон в регулярном выражении regexr.com/83dte — при начальной загрузке будет отображаться ошибка, но если вы удалите символ и добавите его обратно, он запустится.

jmcgriz 16.07.2024 18:18

Возможно, есть лучший способ сделать это (это медленное регулярное выражение и не идеальное), я подумаю об этом, но как о быстром патче, который на данный момент должен делать то, что вы хотите.

jmcgriz 16.07.2024 18:20

Другие вопросы по теме

Похожие вопросы