Извлечение совпадающих адресов с помощью синтаксиса регулярных выражений Google Sheets Re2

Я пытаюсь извлечь все адреса ячеек / диапазонов, которые отображаются в формуле в ячейке Google Таблиц.

Формулы по своей природе могут быть очень сложными. Я перепробовал много шаблонов, которые работают в веб-тестерах, но не в Google таблицах re2.

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

Формула (игнорируйте логику):

=A$13:B4+$BC$12+$DE2+F2:G2

Регулярное выражение:

((\$?[A-Z]+\$?\d+)(:(\$?[A-Z]+\$?\d+))?)

Ожидаемый результат:

[A$13:B4,$BC$12,$DE2,F2:G2]

Здесь (если я правильно понимаю результаты) все выглядит нормально. Я не уверен, что отображаемое совпадение групп также считается совпадением, поскольку указано 4 совпадения, 287 шагов

Однако в таблицах Google возвращаются все результаты Match 1

[A$13:B4,A$13,:B4,B4]    

Остальные совпадения игнорируются. Думаю, вопрос в том, как преобразовать регулярное выражение в синтаксис re2?

Обновление: после комментариев player0, возможно, я не понял. Это всего лишь простой пример, чтобы выделить другие проблемы, которые у меня есть. Это просто строка, содержащая адреса в нескольких относительных и абсолютных форматах. Однако я ищу более широкое общее решение, которое подходит для любых возможных формул, которые могут содержать формулы и ссылки на другие листы. Например:

=(STDEVA(Sheet1!B2:B5)+sum($A$1:$A$2))*B2

Ожидаемый результат Sheet1!B2:B5,$A$1:$A$2,B2

Эта формула содержит две формулы и ссылку на другой лист. Все еще игнорируя здесь именованные диапазоны и другие возможные ссылки на формулы, о которых я в настоящее время не могу думать. Кроме того, квадратные скобки [] не имеют значения, это был просто способ отображения результатов, и он фактически копируется из журналов, как и все это делается в сценарии.


person OJNSim    schedule 11.08.2020    source источник
comment
поделитесь копией своего листа с примером желаемого результата   -  person player0    schedule 12.08.2020
comment
i.stack.imgur.com/EpwaG.png   -  person player0    schedule 12.08.2020
comment
Что делать, если вы не используете никакие группы захвата? \$?[A-Z]+\$?\d+(?::(?:\$?[A-Z]+\$?\d+))? regex101.com/r/A5yKb5/1   -  person The fourth bird    schedule 12.08.2020
comment
@ player0 Думаю, я не совсем понял. Пожалуйста, посмотрите мою правку.   -  person OJNSim    schedule 12.08.2020
comment
@Thefourthbird Поскольку я не являюсь мастером регулярных выражений, я не полностью понимаю использование группировки, однако я понимаю, что это предотвратит сопоставление групп. Во всяком случае, в таблицах Google по-прежнему возвращается только первое совпадение A$13:B4   -  person OJNSim    schedule 12.08.2020


Ответы (3)


пытаться:

=INDEX(SUBSTITUTE(TEXTJOIN(",", 1, 
 IFNA(REGEXEXTRACT(SPLIT(SUBSTITUTE(FORMULATEXT(A3), "'", "♥"), 
 "+-*/^()=<>&"), 
 "(?:.+!)?[A-Z$]+\d+(?::[A-Z$](?:\d+)?)?|(?:.+!)?[A-Z$]:[A-Z$]+"))), "♥", "'"))

введите описание изображения здесь


или дольше:

=INDEX(SUBSTITUTE(TEXTJOIN(",", 1, 
 IFNA(IFNA(REGEXEXTRACT(SPLIT(SUBSTITUTE(FORMULATEXT(A3), "'", "♥"), 
 "+-*/^()=<>"), "(?:.+!)?[A-Z$]+\d+(?::[A-Z$](?:\d+)?)?"), 
 REGEXEXTRACT(SPLIT(SUBSTITUTE(FORMULATEXT(A3), "'", "♥"), 
 "+-*/^()=<>"), "(?:.+!)?[A-Z$]:[A-Z$]+")))), "♥", "'"))

введите описание изображения здесь

person player0    schedule 12.08.2020
comment
@OJNSim это должно покрыть все ваши потребности - person player0; 12.08.2020
comment
Я понимаю, что вы сделали, я только не понимаю, почему это решило проблему или, лучше сказать, почему возникла проблема вообще. То, что возвращается только первое совпадение, как себя ведет re2? - person OJNSim; 13.08.2020
comment
Кроме того, я на самом деле делаю это внутри сценария, а не с помощью внутренних функций. Итак, вы предлагаете разбить весь текст формулы в массив и перебрать последний, чтобы (попытаться) сопоставить каждую запись? Чтобы уточнить, прямой подход не работал ни с внутренней функцией, ни со сценарием (с чего я начал). - person OJNSim; 13.08.2020
comment
Кроме того, как я прокомментировал ответ Яна, при удалении /g флага он также возвращает только первый матч. Так, может быть, у re2 есть такой флаг, и в этом проблема? - person OJNSim; 13.08.2020
comment
не уверен, что ваш последующий вопрос о ... регулярное выражение всегда будет возвращать только указанное количество групп, поэтому необходимо разделить ввод, потому что количество совпадений является переменным. regex101 - хороший инструмент, но совершенно бесполезный в сочетании с таблицами Google. - person player0; 13.08.2020

Кажется, можно использовать

[A-Z$]+\d+(?::[A-Z$]\d+)?

См. демонстрацию на regex101.com.

person Jan    schedule 12.08.2020
comment
Хотя он удалил все совпадения группировки, он по-прежнему возвращает только первое совпадение A$13:B4. Теперь я понимаю, что это может быть глобальный флаг \g в демонстрации. Есть ли re2 эквивалентный флаг? - person OJNSim; 12.08.2020

Я придумал лучший способ без разделения, используя флаг /g. Однако это работает в сценарии, а не с использованием внутренней функции регулярного выражения Sheets (т.е. REGEXEXTRACT), поскольку я не мог понять, как отформатировать строку регулярного выражения в ячейке, которая будет содержать флаг / g, и REGEXEXTRACT будет принимать как действительный регулярное выражение.

Вот код:

/* Find all predessesor cells of input range 
*/
function findPredecessor(rng){
 
  var formualaText = rng.getFormula();
  
  /* addMatchesRegex
  * supports all A1Notation addresses 
  * the 2nd regex after the | operator will match all column addresses (A:A, Sheet2!b:B, etc)
  * some NamedRanges with names like NameRange1 
  * Does not support - NamedRange with names including dot, not ending with digits 
  */
  var addMatchesRegex = /(([\w .'!]+)?(\$?[A-Z]+\$?\d+\b)(:(\$?[A-Z]+\$?\d+))?)|([\w .'!]+)?[A-Z]+:[A-Z]+/gi; 
     
  var addMatches = formualaText.match(addMatchesRegex);
  
  Logger.log("%s add matched: %s",addMatches.length,addMatches);
  
  /* fullMatchRegex
  *  modify addMatches to return also strings like
  * 1. SUM, IFERROR, etc - internal sheets functions.
  * 2. NamedRanges
  * 
  */
  var fullMatchRegex = /(([\w .'!]+)?([\$A-Z.\d]*)(:(\$?[A-Z]+\$?\d*))?)/gi; 
  
  // match regex with formula
  var fullMatches =  formualaText.match(fullMatchRegex);
    
  Logger.log("Full matches list: %s",fullMatches);
  
  var namedRangesAdd = analyzeMatch(addMatches,fullMatches);
    
  Logger.log("%s total predecessors: %s",namedRangesAdd.length,namedRangesAdd);
}



/* This function accepts the two regex matches list
*  and returns one unique list of all predecessor addresses
*  @param {Array} addMatches - All A1 notation addresses 
*                              plus some of NamedRanges 
*  @param {Array} fullMatches - All A1 notation addresses,All NamedRanges,
*                               Other irrelevent matches
*/
function analyzeMatch(addMatches,fullMatches){

  /*Expected 
    First parameter - holds all A1Notation addresses as well as NamedRanges that
    their name in the form of /[A-Z]+/d+
    NamedRange with name including dot(.) or does not contain digits will not
    be on the list
    Second Parameter - contains all first list matches, as well as all NamedRanges
    names and also irrelevant matches to be filtered like function names and empty string 
  */
  
  //Full Matched Addresses to be returned
  var mAddresses = [];
  
  //Remove duplicate addresses
  var uniqueMatches = 
      addMatches.filter((item,index)=>addMatches.indexOf(item)===index); 
  
  //Get all named Ranges in spread sheet
  var nr = SpreadsheetApp.getActive().getNamedRanges();
  
  // Loop Named Ranges arr 
  nr.forEach(function(item){
  
    /* Check if the name of the current Named Range
    * is included in matches
    * 1. first in addMatches list
    * 2. only if not found in the wider list */
    
    var name = item.getName();
    
    //Check if in addmatches array
    var i = uniqueMatches.indexOf(name);
    
    //Build A1Notation address of current NamedRange 
    var rng = item.getRange();
    var add = "'" + rng.getSheet().getName() + "'!" + rng.getA1Notation();    
    
    if (i > -1){
      
      //Add the address of curr NamedRange to final list 
      mAddresses.push(add);
      //Remove curr NamedRange from list
      uniqueMatches.splice(i,1);
      
    }else if (fullMatches.includes(name)){
      // Name found - add the address of the 
      //              Named Range to matched Addresses list
      
      mAddresses.push(add);    
    }
    
  });
  
  //Add all left matched addresses to final list  
  mAddresses.push(...uniqueMatches);
  
  return mAddresses;
   
}

Что немного усложняет, так это NamedRanges. Этот код сопоставляет, анализирует и возвращает один список со всеми адресами предшественников, включая адреса NamedRanges.

person OJNSim    schedule 24.08.2020