Javascript (распознавание ошибок для строк типа данных), сопоставьте слово, которое должно быть в кавычках

avatar
Hanna Rose
8 августа 2021 в 20:42
146
4
-1

Привет, я пытаюсь создать редактор кода javascript, я предполагаю, что вы знаете, как создаются строковые типы данных в javascript, поэтому я хочу, чтобы каждое слово было внутри символа ("), "двойных кавычек" или (') "одинарные кавычки" в HTML-тег i.
поэтому я создал ситуацию хаоса и попытался сопоставить некоторые слова, которые должны быть внутри кавычек, как мои ожидания.
это моя строка string:

`that's "that's" 'that's'. Hanna "loves" 'ice "cream`

получить такой вывод:

that's <i>that's</i> <i>that's</i>. Hanna <i>loves</i> ice cream

let str_ = `that's "that's" 'that's'. Hanna "loves" 'ice "cream`;
// expected output :
// that's <i>that's</i> <i>that's</i>. Hanna <i>loves</i> ice cream
Источник
evolutionxbox
8 августа 2021 в 20:43
6

Как вы определяете разницу между ' для курсива и ' для грамматики (например, that's)?

t.niese
8 августа 2021 в 20:45
2

I have seen the answers on similar problems but it doesn't work что является примером таких ответов здесь, на SO, и почему они не сработали для вас?

Hanna Rose
8 августа 2021 в 20:46
0

@evolutionxbox это просто возможное состояние ошибки. вы можете изменить слово на не

t.niese
8 августа 2021 в 20:50
0

Но почему 's "that' не должно быть строкой? Но 'test' является допустимым. И почему значение для 'that's' должно быть that's, а не просто that?

Hanna Rose
8 августа 2021 в 20:54
0

@t.niese, это проблема, с которой я столкнулся. как получить вывод, как я ожидаю в таком состоянии.

t.niese
8 августа 2021 в 21:01
2

В показанном тексте нет правила, и это неоднозначно. Вы не можете определить для этого текста, является ли кавычка частью текста или предназначена для цитирования строки. Вот почему вы не увидите такой конструкции в языке программирования. В JavaScript у вас может быть let str ="something ' something ' something"; или let str ='something " something " something';, но let str ='that's something'; не является допустимым JavaScript.

myf
8 августа 2021 в 21:14
0

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

t.niese
8 августа 2021 в 21:17
0

Вы можете делать наилучшие предположения, но не создавать надежный парсер для таких строк. За исключением случаев, когда вы определяете для них очень четкие ограничения, но даже в этом случае это может быть проблематично. Вот почему языки программирования используют управляющие последовательности. Если вы хотите использовать " в строковых кавычках с ", вам нужно экранировать его с помощью \. Это, например. одна из причин, по которой синтаксический анализ естественных языков, таких как английский, настолько проблематичен.

Hanna Rose
8 августа 2021 в 22:46
0

это для функции распознавания ошибок в моем редакторе кода. поэтому я создал ситуацию хаоса и убедился, что мое приложение справится с этим. спасибо

Ответы (4)

avatar
Peter Seliger
8 августа 2021 в 21:23
1

Подход, который исходит из примера OP как само собой разумеющееся, что начало и конец действительной цитаты всегда сопровождаются по крайней мере одним начальным и по крайней мере одним конечным пробелом, может привести к регулярному выражению, подобному этому /(?<space>\s+)(?<quote>['"])(?<text>\S+)\2/gm. Он использует группы захвата в своей именованной форме. Следующее регулярное выражение идентично первому, только без именования ... /(\s+)(['"])(\S+)\2/gm. Читается так...

  • 1-я группа захвата (\s+)

    • \s соответствует любому символу пробела (эквивалентно [\r\n\t\f\v ])
    • + соответствует предыдущему токену от одного до неограниченного количества раз, столько раз, сколько возможно, возвращая по мере необходимости (жадный)
  • 2-я группа захвата (['"])

    • Соответствует одному символу из списка ниже ['"]
      • '" соответствует одному символу в списке (с учетом регистра)
  • 3-я группа захвата (\S+)

    • \S соответствует любому непробельному символу (эквивалентно [^\r\n\t\f\v ])
    • + соответствует предыдущему токену от одного до неограниченного количества раз, столько раз, сколько возможно, возвращая по мере необходимости (жадный) \2 соответствует тому же тексту, который последний раз соответствовал второй группе захвата
  • Флаги глобального шаблона

    • Модификатор g: глобальный. Все совпадения (не возвращать после первого совпадения)
    • Модификатор m: многострочный. Заставляет ^ и $ соответствовать началу/концу каждой строки (не только началу/концу строки)

Для каждого совпадения функция обратного вызова заменителя String.prototype.replace предоставляет через свои аргументы сопоставленные/захваченные данные, которые будут необходимы для возвращаемого значения замены. В этом случае можно получить доступ к (match, space, quote, text), что позволяет вернуть ${ space }<i>${ text }</i> в качестве вычисленного значения замены.

Поскольку эта замена не очищает поврежденные/недействительные кавычки, после них необходимо выполнить дополнительную замену (на основе /(\s+)(['"])/gm), как показано в журналах приведенного ниже примера кода.

// // [https://regex101.com/r/frH2xQ/1] 
// const regXSpacedAndQuotedChars = (/(?<space>\s+)(?<quote>['"])(?<text>\S+)\2/gm);

// [https://regex101.com/r/frH2xQ/2] 
const regXSpacedAndQuotedChars = (/(\s+)(['"])(\S+)\2/gm);

// [https://regex101.com/r/frH2xQ/3/]
const regXSpacedAndSoleQuote = (/(\s+)(['"])/gm);

console.log(
  `that's "that's" 'that's'. Hanna "loves" 'ice "cream`
    .replace(regXSpacedAndQuotedChars, (match, space, quote, text) => `${ space }<i>${ text }</i>`)
);

console.log(
  `that's "that's" 'that's'. Hanna "loves" 'ice "cream`
    .replace(regXSpacedAndQuotedChars, (match, space, quote, text) => `${ space }<i>${ text }</i>`)
    .replace(regXSpacedAndSoleQuote, (match, space, quote) => space)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
t.niese
8 августа 2021 в 21:40
2

Я согласен, что это будет работать для показанных случаев. Но, учитывая текст в вопросе I'm trying to create a javascript code editor,, я бы предположил, что на практике это не удастся. Это будет работать только в том случае, если между кавычками нет пробелов.

Peter Seliger
8 августа 2021 в 21:58
1

@t.niese ... Это правильно. Это было причиной того, что я проголосовал за два комментария по этому вопросу, ваш и один из evolutionxbox, прежде чем я начал работать над ответом выше.

Hanna Rose
8 августа 2021 в 22:48
0

@Peter Seliger, это функция распознавания ошибок в моем редакторе кода. поэтому я создал эту ситуацию хаоса и убедился, что мое приложение справится с ней. спасибо за ваше решение, сэр.

Peter Seliger
8 августа 2021 в 22:49
0

@HannaRose ... 1/2 ... Знакомство с регулярным выражением похоже на получение мощной волшебной палочки. Нужно знать заклинания (его грамматику). Но нужно также знать, когда и особенно когда его не использовать. (Это похоже на попытку написать код синтаксического анализатора, полностью основанный на регулярных выражениях, что похоже на настоящую черную магию.)

Peter Seliger
8 августа 2021 в 22:50
0

@HannaRose ... 2/2 ... Таким образом, в этом вопросе следует следовать за некоторыми хорошими учителями, такими как, например. Виктор Стрибижев или Четвертая птица

Hanna Rose
8 августа 2021 в 23:05
0

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

avatar
myf
9 августа 2021 в 03:33
1

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

В качестве упражнения я попытался сделать что-то похожее на этот подход, с правилами, абстрагированными из примеров OP, где только «корневой уровень, слова, заключающие кавычки», считаются релевантными, а «висячие» кавычки отбрасываются. Итак, правила:

  • первое вхождение любой кавычки после перехода символа, отличного от слова, в состояние "кавычка", где:
    • остальные цитаты передаются дословно,
    • те же кавычки, за которыми не следуют символы, не являющиеся словами, передаются дословно
    • та же кавычка, за которой следует символ, не являющийся словом, завершает кавычку и выходит из состояния кавычки.
  • Открытая, но незаконченная цитата отбрасывается. (Это нарушает вариант использования OP, потому что отбрасываются все оборванные «вложенные» кавычки, что, по ИМО, значительно усложнит конечный автомат. И я оставил его, чтобы сделать этот случай более очевидным. Такое явление обнаружено и сообщается в консоли.)

В качестве примера я намеренно избегал регулярных выражений даже для определения несловного символа, который я сделал коротким (пробел и ,.!?\/-, плюс кавычки " и ', которые обрабатываются отдельно).

Пожалуйста, примите это скорее как POC: я раньше не писал FSM, так что, скорее всего, это не лучший учебный материал.

// @ts-check
function state_machine(input_str) {
  var input_buffer = [...input_str];
  var out = [];
  var char = '';
  var start_quote_adept_index = -1;
  var quote_kind;

  function is_word_char() {
    if (!char) {
      return false;
    }
    if (" ,.!?\\/-".includes(char)) {
      return false;
    }
    return true;
  }

  function is_quote() {
    if (char == "'" || char == '"') {
      return true
    }
    return false
  }

  function add(ch) {
    out.push(ch);
  }
  var states = {
    outside_word: function() {
      if (is_quote()) {
        state = states.in_quote;
        quote_kind = char;
        start_quote_adept_index = i;
        add('');
      } else if (is_word_char()) {
        state = states.in_word
        add(char);
      } else {
        add(char);
      }
    },
    in_quote: function() {
      if (char == quote_kind) {
        state = states.maybe_after_quote;
      } else {
        add(char);
      }
    },
    maybe_after_quote: function() {
      if (is_word_char()) {
        // false alarm: quote inside word
        state = states.in_quote;
        add(quote_kind);
        add(char);
      } else {
        // yup, "proper" quote
        state = states.outside_word;
        out[start_quote_adept_index] = '<i>';
        add('</i>');
        start_quote_adept_index = -1;
        add(char);
      }
    },
    in_word: function() {
      if (is_word_char()) {
        add(char);
      } else {
        state = states.outside_word;
        add(char);
      }
    }
  }

  var state = states.outside_word;
  var i = -1;
  while (++i <= input_buffer.length) {
    //     <= for one extra assignment of `undefined` for finishing state
    char = input_buffer[i];
    state();
  }
  if (start_quote_adept_index > -1) {
    console.warn('Non-balanced quote `%s` at index %s', quote_kind, start_quote_adept_index);
    var err_info = [...input_str];
    err_info.splice(start_quote_adept_index + 1, 0, '%c');
    err_info.splice(start_quote_adept_index, 0, '%c');
    console.info(err_info.join(''), 'border: 1em solid red');
  }
  return out.join('');
}

input.oninput()
<input id="input" oninput="console.clear();output.value=state_machine(this.value)" size="80" value="that hat's &quot;that hat's&quot; 'that hat's'. Hanna &quot;loves&quot; '' 'ice &quot;cream">
<br><output id="output" style="white-space: pre-wrap"></output>

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

OTOH, этот подход без проблем обрабатывает слова и даже предложения внутри кавычек.

Основой этого подхода является цикл, который постепенно вызывает функцию состояния для каждого символа из входной строки (плюс один дополнительный), который проверяет символ, в конечном итоге добавляет его в выходной буфер и устанавливает следующее состояние. Для открывающих кавычек он просто сохраняет свой индекс и добавляет '' к выводу до тех пор, пока не будет соответствующего закрывающего аналога, затем открывающий тег вставляется в сохраненную позицию индекса. Я не уверен, что это лучшая практика, но я думаю, что любой другой способ приведет к ненужным осмотрам. Таким образом, он остается в (я думаю) желанной O(n) земле.


(*) Основными преимуществами простого FSM по сравнению с регулярным выражением являются, в основном, предотвращение различных катастрофических опасностей возврата и очень трудная читаемость и "отладка" литералов регулярных выражений.

Peter Seliger
9 августа 2021 в 09:48
0

Спасибо за труд и подробное объяснение

avatar
Hanna Rose
8 августа 2021 в 22:24
0

это мое предыдущее решение. но я нашел лучшее решение в Ответ Петра Селигера. Спасибо

function isValidQuotes(word, dir_){
  const wlength = word.length;
  for(let n=0; n<wlength; n++){
    let a = (dir_==='<') ? wlength-n-1 : (dir_==='>') ? n:false;
    if(a === false) return false;
    if( isLetter(word[a]) === false ){
      if( ["'",'"'].indexOf(word[a]) !== -1 ){ return true; }
    }else{
      return false;
    }
  }
}
function isLetter(n) {
  return (n.match(/[A-Za-z0-9]/i) !== null && n !== ' ') ?true:false;
}

let str_ = `that's "that's" 'that's'. Hanna "loves" 'ice "cream`;
// expected output :
// that's <i>that's</i> <i>that's</i>. Hanna <i>loves</i> ice cream
let res = "";
for( let a of str_.split(" ") ){
  if( 
  isValidQuotes(a, '>') === true && isValidQuotes(a, '<') === true
  ){
    res += '<b>'+ a +'</b> ';
  }else{
    res += a+' ';
  }
}
console.log(res);
avatar
SharedRory
8 августа 2021 в 21:18
-1

Это не то, что вам нужно, но это имеет смысл https://regex101.com/r/PdsWVL/2. По сути, просмотрите строку и найдите первое вхождение символа " или ', а затем продолжайте, пока не найдете закрывающее, а затем повторите с этой позиции.

t.niese
8 августа 2021 в 21:19
4

go through the string and find the first occurrence of either a " or a ' and then continue until you find a closing оператор хочет преобразовать 'that's' в <i>that's</i>., так как же определить, что ' перед s не является закрывающим?