Regex question lazarus / 61-Beginners ---------------------------------------------------------------------- justnewbie · 2019-07-15 17:29 Hi guys, I have this text: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i something retvalue -1 anything anyt=55; nothing var1=5; By using the RegExpr, I want to get the text in this form (ie. the anything and nothing should be in a new line): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i something retvalue -1 anything anyt=55; nothing var1=5; The https://regex101.com/ site gives me the good result if I write the \n\0 in the substitution field (see the attached picture). But the Lazarus cannot handle it. My code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i reBefore := '(anything|nothing)';reAfter := '\n\0';Memo1.Text := ReplaceRegExpr(reBefore, Memo1.Text, reAfter, true); This code doesn't do what I need. How can I solve this? ---------------------------------------------------------------------- engkin · 2019-07-15 17:35 replace '\n\0' with LineEnding ---------------------------------------------------------------------- justnewbie · 2019-07-15 17:41 Quote from: engkin on July 15, 2019, 07:35:37 pmreplace '\n\0' with LineEnding Do you mean Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i reAfter := LineEnding;? This doesn't work. ---------------------------------------------------------------------- engkin · 2019-07-15 17:49 Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1; {$mode objfpc}{$H+} uses  RegExpr; var  s1:string='something retvalue -1 anything anyt=55; nothing var1=5;';  s2, reBefore, reAfter: String;begin  reBefore := '(anything|nothing)';  reAfter := LineEnding+'$0';  s2 := ReplaceRegExpr(reBefore, s1, reAfter, true);  WriteLn(s2);  ReadLn;end. ---------------------------------------------------------------------- justnewbie · 2019-07-15 18:09 Great, thank you, works! If I may ask, why did not work the original version? >> '\n\0' What does '$0' mean? Also, I tried this way Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i reAfter := '\n$0';and it worked as well. ---------------------------------------------------------------------- engkin · 2019-07-15 18:41 Quote from: justnewbie on July 15, 2019, 08:09:03 pmGreat, thank you, works! why did not work the original version? >> '\n\0' It did work, but did not give what you want. Quote from: justnewbie on July 15, 2019, 08:09:03 pmWhat does '$0' mean? It refers to the text inside the brackets, in this expression it may be anything or nothing. To see it, try this change: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   reAfter := '-->$0<--'; Quote from: justnewbie on July 15, 2019, 08:09:03 pmAlso, I tried this way Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i reAfter := '\n$0';and it worked as well. I just checked RegExpr code, it supports: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i      't': Result := #$9;  // \t => tab (HT/TAB)     'n': Result := #$a;  // \n => newline (NL)     'r': Result := #$d;  // \r => carriage return (CR)     'f': Result := #$c;  // \f => form feed (FF)     'a': Result := #$7;  // \a => alarm (bell) (BEL)     'e': Result := #$1b; // \e => escape (ESC)     'x': begin // \x: hex char ---------------------------------------------------------------------- justnewbie · 2019-07-15 18:47 Thank you for your help, engkin! ---------------------------------------------------------------------- justnewbie · 2019-07-16 09:58 Now I have a bit more complicated task. In my example there is a loop "header" that is in 3 lines, and I need it only in 1 line. The original text: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i    string something="";   bool anything=true;   for(int i=0;   i=var1; var4--) and similar ... By using this regex for\s*\([^\n]+\n[^\n]+\n[^\n]+ I can get the loop "header", but I don't know how could I get the result without those 2 \n (highlighted with orange). Any help? ---------------------------------------------------------------------- engkin · 2019-07-16 11:35 To remove all line endings use: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   reBefore := '([^\r\n]+)[\r\n]+';  reAfter := '$1';  s2 := ReplaceRegExpr(reBefore, s1, reAfter, true); To remove the two line endings in a for statement: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   reBefore := '(for\s*\([^\r\n]+)[\r\n]+([^\r\n]+)[\r\n]+([^\r\n]+\))';  reAfter := '$1$2$3';  s2 := ReplaceRegExpr(reBefore, s1, reAfter, true); ---------------------------------------------------------------------- justnewbie · 2019-07-16 12:06 Thank you engkin, this is exactly what I need for! (Soon I will have new question ...) ---------------------------------------------------------------------- justnewbie · 2019-07-16 14:19 New issue: I need those lines that do not start with the word 'something'. I know what ^something does, but I need for its opposite. Edit: maybe I figured it out: ^[^\bsomething\b].* ---------------------------------------------------------------------- justnewbie · 2019-07-16 15:09 Next: I have this text: Quotemytext mytext hjsdgfjsh 57574 //gkgaksg //  ztwojkgk fjfjaf//tjtrjs nodelete fghfgkhag //zrwjsj ghfhfhd ://uzrutziut I want to delete the texts in red (text after the //, together the // itself), but I don't want to delete the texts in teal. Ie., no delete if 1./ a line starts with nodelete or 2./ there is a colon (:) right before the // So I want to get this: Quotemytext mytext hjsdgfjsh 57574 fjfjaf nodelete fghfgkhag //zrwjsj ghfhfhd ://uzrutziut Update: I found the solution for the 2. case: (? '{') or (aTxt[Length(aTxt)] <> '}') then    Exit;  sl := TStringList.Create;  try    sl.Text := aTxt;    if sl.Count <> 6 then      Exit;    c := Pos(',', sl[1]);    if c = 0 then      Exit;    Dec(c);    s1 := Trim(Copy(sl[1], 1, c));     c := Pos(',', sl[2]);  if c = 0 then      Exit;    Dec(c);    s2 := Trim(Copy(sl[2], 1, c));     c := Pos('/', sl[3]);    if c = 0 then      Exit;    Dec(c);    s3 := Trim(Copy(sl[3], 1, c));     s4 := Trim(sl[4]);    Result := True;  finally    sl.Free;  end;end; ---------------------------------------------------------------------- justnewbie · 2019-10-11 16:50 Thank you, but unfortunately it is not good for me. I need REGEX. (For example: I don't know in advance how many strings will be there, the 4 is just an example. So, I need a general solution, regex can do it.) ---------------------------------------------------------------------- ASerge · 2019-10-11 23:06 Quote from: justnewbie on October 11, 2019, 06:50:57 pmThank you, but unfortunately it is not good for me. I need REGEX. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$MODE OBJFPC}{$APPTYPE CONSOLE}{$LONGSTRINGS ON} uses RegExpr; procedure Test(const S: string);var  R: TRegExpr;begin  R := TRegExpr.Create('');  try    R.ModifierM := True;    R.Expression := '^\W*(\w+)';    if R.Exec(S) then      repeat        Writeln('"', R.Match[1], '"');      until not R.ExecNext;  finally    R.Free;  end;end; const  CSampleInputText =    '{' + LineEnding +    '   First,//Something text' + LineEnding +    '   Second,' + LineEnding +    '   Small//Something text' + LineEnding +    '   Big' + LineEnding +    '}';begin  Test(CSampleInputText);  Readln;end. ---------------------------------------------------------------------- howardpc · 2019-10-12 09:19 Quote from: justnewbie on October 11, 2019, 06:50:57 pmI don't know in advance how many strings will be there, the 4 is just an example. So, I need a general solution, regex can do it.A more general (non-regex) solution might be something like the following. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program ParseExample; {$AppType console}{$Mode objfpc}{$H+}        uses Classes, SysUtils; const  txt = '{' + LineEnding +        '   First,//Something text' + LineEnding +        '   Second,' + LineEnding +        '                          extra                 ' + LineEnding +        '   Small//Something text' + LineEnding +      '   Big' + LineEnding + '}'; function ParsedCommentToFirstWords(const aTxt: String; list: TStrings): Boolean;     function FirstWord(const s: String): String;    var      p, b: Integer;    begin      p := 1;      while (s[p] in [' ', #9]) and (p < Length(s)) do        Inc(p);      b := p;      while (s[p] in ['a'..'z','A'..'Z']) and (p < Length(s)) do        Inc(p);      case (p = Length(s)) and (s[p] in ['a'..'z','A'..'Z']) of        True:  Result := Copy(s, b, p-b+1);        False: Result := Copy(s, b, p-b);      end;    end; var  i, min: Integer;begin  list.Text := Trim(aTxt);  if (list.Count < 3) or (Trim(list[0]) <> '{') or (Trim(list[list.Count-1]) <> '}') then    Exit(False);  list.Delete(0);  list.Delete(list.Count-1);  min := MaxInt;  for i := 0 to list.Count-1 do    begin      list[i] := FirstWord(list[i]);    if min > Length(list[i]) then        min := Length(list[i]);    end;  Result := min > 0;end; var  sl: TStringList;  s: String;begin  sl := TStringList.Create;  if ParsedCommentToFirstWords(txt, sl) then    for s in sl do      WriteLn(s)  else Writeln('invalid text format for "',txt,'"');  sl.Free;end. ---------------------------------------------------------------------- justnewbie · 2019-10-14 20:33 Guys, thank you very much! ---------------------------------------------------------------------- justnewbie · 2019-10-15 13:49 Hi ASerge, I wish you were somewhere nearby ...  I have a task that is too complicated for me (again! LOL). I spent hours with it, but ... I have a text and I need to get all the variable names (highlighted with bold). What is the proper REGEX pattern to get them? Have you any idea? Quoteint something; double anything=2.15; int nothing  = 7; string anytext, mytext="",poems; int   x=7;int z; int w=85; int k,q,e=2; ---------------------------------------------------------------------- Thaddy · 2019-10-15 14:02 Regular expressions are powerful, but that looks like something where they are not in place. It is much simpler to compare to a list of reserved words(int,double etc, not something and family) and parse until white space or control is met ---------------------------------------------------------------------- justnewbie · 2019-10-15 14:10 Quote from: Thaddy on October 15, 2019, 04:02:43 pmRegular expressions are powerful, but that looks like something where they are not in place. It is much simpler to compare to a list of reserved words(int,double etc, not something and family) and parse until white space or control is met I don't understand your post. I need to get the variable names (something, anything etc ..) that are highlighted with bold. ---------------------------------------------------------------------- lucamar · 2019-10-15 14:30 Quote from: justnewbie on October 15, 2019, 04:10:36 pmQuote from: Thaddy on October 15, 2019, 04:02:43 pmRegular expressions are powerful, but that looks like something where they are not in place. It is much simpler to compare to a list of reserved words(int,double etc, not something and family) and parse until white space or control is met I don't understand your post. I need to get the variable names (something, anything etc ..) that are highlighted with bold. It means parsing out the known reserved words, numbers, etc. so that you're left with the "unknown" ones, which are what you're after. For example, parsing the second line: double anything=2.15; you first get "double" which, being a reserved word, you can ignore; then you skip the space(s) and get the word up to the symbol "=": you get "anything" which is not a reserved word but comes after one, so it must be a variable name, which is what you're looking for. Keep going on, skipping the parts in which you're not interested, and you get your list of variables as result. It might help if you draw a BNF diagram of your lines; that allows you to get a "feel" of what (and how) to parse "in" and what "out". ---------------------------------------------------------------------- justnewbie · 2019-10-15 14:40 Quote from: lucamar on October 15, 2019, 04:30:39 pmQuote from: justnewbie on October 15, 2019, 04:10:36 pmQuote from: Thaddy on October 15, 2019, 04:02:43 pmRegular expressions are powerful, but that looks like something where they are not in place. It is much simpler to compare to a list of reserved words(int,double etc, not something and family) and parse until white space or control is met I don't understand your post. I need to get the variable names (something, anything etc ..) that are highlighted with bold. It means parsing out the known reserved words, numbers, etc. so that you're left with the "unknown" ones, which are what you're after. For example, parsing the second line: double anything=2.15; you first get "double" which, being a reserved word, you can ignore; then you skip the space(s) and get the word up to the symbol "=": you get "anything" which is not a reserved word but comes after one, so it must be a variable name, which is what you're looking for. Keep going on, skipping the parts in which you're not interested, and you get your list of variables as result. It might help if you draw a BNF diagram of your lines; that allows you to get a "feel" of what (and how) to parse "in" and what "out". To be honest, I cannot imagine that there is no a much simpler way. Think of it: there are hundreds of reserved words and characters (my example is heavily simplified). This is where I am now: I can get the "orange parts" that contain those names (see picture). The pattern: \b(int|double|string)(\s+)(.+)(  >> the smiley is a ; and ) But, I don't know how could I get ONLY the names. ---------------------------------------------------------------------- Thaddy · 2019-10-15 14:51 Quote from: justnewbie on October 15, 2019, 04:40:04 pmTo be honest, I cannot imagine that there is no a much simpler way. Think of it: there are hundreds of reserved words and characters (my example is heavily simplified). It is NOT that easy! As Lucamar confirmed. For such tasks I usually write a compiler... Don't be afraid, I mean I write a grammar and use plex and pyacc or GoldParser to  generate the basic code. In your case any C grammar would generate a correct lexer and parser for your problem so you don't even have to write a grammar...... Now that is a "nice" answer to anyone that thinks simple things must be easy to program.. There is a good example: the h2pas sourcecode. (I mean that: you have to do all that) ---------------------------------------------------------------------- justnewbie · 2019-10-15 17:20 It can be done with 2 or 3 steps, see pictures. ---------------------------------------------------------------------- howardpc · 2019-10-15 17:59 A non-regex solution could be done by extending the list of allowed type names in the following example. I don't know if digits are allowed in your variable names. If so, you'll need to adjust the parsing routine accordingly. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program project1; {$mode objfpc}{$H+}{$IfDef Windows}{$AppType console}{$EndIf} uses  SysUtils, Types; function IsReserved(const aWord: String): Boolean; // extend this for needed keywordsbegin  if not Length(aWord) in [3, 6] then    Exit(False);  case LowerCase(aWord) of    'int',    'string',    'double': Exit(True);  else      Exit(False);  end;end; function GetVarNames(aTxt: String): TStringDynArray;var  p: Integer = 1;  index: Integer = 0;  s: String;   function GetNextWord: String; // assumes variable names must be alphabetical  begin    Result := '';    while (p < Length(aTxt)) and not (aTxt[p] in ['A'..'Z','a'..'z']) do      Inc(p);    while (p < Length(aTxt)) and (aTxt[p] in ['A'..'Z','a'..'z']) do      begin      Result := Result + aTxt[p];        Inc(p);      end;  end; begin  SetLength(Result, Length(aTxt) shr 1);  aTxt := Trim(aTxt);  repeat  s := GetNextWord;    case IsReserved(s) of      True: ;      False:      begin          Result[index] := s;          Inc(index);        end;    end;  until s = '';  SetLength(Result, Pred(index));end; var  txt: String = 'int something;'+LineEnding+                'double anything=2.15;' + LineEnding +                'int nothing  = 7;' + LineEnding +                'string anytext, mytext="",poems;' + LineEnding +                'int   x=7;int z; int w=85;' + LineEnding +                'int k,q,e=2;';  arr: TStringDynArray;  s: String; begin  arr := GetVarNames(txt);  for s in arr do    WriteLn(s);  WriteLn('Press [Enter] to finish');  ReadLn;end.This outputsCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i somethinganythingnothinganytextmytextpoemsxzwkqePress [Enter] to finish ---------------------------------------------------------------------- Thaddy · 2019-10-15 18:17 That's extending white space...(but it neat code and maybe is a solution) The only proper solution is a parser. ---------------------------------------------------------------------- justnewbie · 2019-10-15 18:36 Thank you guys for the contribution! ---------------------------------------------------------------------- bytebites · 2019-10-15 19:36 Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Result := Result + aTxt[p];This is slow. ---------------------------------------------------------------------- howardpc · 2019-10-22 14:32 A non-regex solution: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program project1; {$mode objfpc}{$H+}{$IfDef Windows}{$AppType console}{$EndIf} uses  SysUtils; var  txt: String = 'Something (anything nothing="hey!", anything something, nothing="hola!", thing)' +                'something nothing="aloha!"';  sArr: TStringArray;  s: String;  b, e, i: Integer;   function ExtractedBrackets(const aTxt: String; out Brackets: TStringArray): Boolean;  var    p, pb: Integer;    bCount: Integer = 0;  begin    SetLength({%H-}Brackets, 0);    Result := False;    p := 0;    while p < Length(aTxt) do      begin      Inc(p);        if (aTxt[p] = '(') and (bCount = 0) then          begin            Inc(bCount);            pb := Succ(p);          end;        if (aTxt[p] = ')') and (bCount > 0) then          begin          SetLength(Brackets, Length(Brackets)+1);            Brackets[High(Brackets)] := Copy(aTxt, pb, p-pb);            Dec(bCount);            Result := True;          end;      end;  end; function FoundBetweenPatternAndSeparator(aBegin: Integer; const aTxt, aPattern: String; aSeparators: TSysCharSet; out Fragment: String; out EndPos: Integer): Boolean;var  p, b: Integer;begin  Fragment := '';  Result := False;  EndPos := 0;  p := Pos(aPattern, aTxt, aBegin);  if p > 0 then    begin      Inc(p, Length(aPattern));      b := p;      repeat        Inc(p);      until (p = Length(aTxt)) or (aTxt[p] in aSeparators);      if aTxt[p] in aSeparators then      begin          Fragment := Copy(aTxt, b, p-b);          EndPos := Succ(p);          Exit(True);        end;    end;end; begin  b := 1;  if ExtractedBrackets(txt, sArr) then    for i := 0 to High(sArr) do    while FoundBetweenPatternAndSeparator(b, sArr[i], 'nothing=', [','], s, e) do        begin          WriteLn(s);          b := e;        end;  ReadLn;end. ---------------------------------------------------------------------- ASerge · 2019-10-22 21:04 Quote from: justnewbie on October 22, 2019, 01:00:52 pmI want to find all occurences of a pattern in a text. Requirements: follows the nothing= and has to be within brackets. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$MODE OBJFPC}{$APPTYPE CONSOLE}{$LONGSTRINGS ON} uses RegExpr; procedure Test(const S: string);var  ROut, RIn: TRegExpr;begin  ROut := TRegExpr.Create('\(([^\)]*)\)');  try    if ROut.Exec(S) then    begin      RIn := TRegExpr.Create('nothing="([^"]+)"');      try      repeat          if RIn.Exec(ROut.Match[1]) then            repeat            Writeln(RIn.Match[1]);            until not RIn.ExecNext;      until not ROut.ExecNext;      finally        RIn.Free;      end;  end;  finally    ROut.Free;  end;end; const  CSampleInputText =    'Something (anything nothing="hey!", anything something, nothing="hola!", thing)' + LineEnding +    'something nothing="aloha!"' + LineEnding +    '(skip empty nothing="")' + LineEnding +    '(other nothing="other!", nothing="again!", thing)';begin  Test(CSampleInputText);  Readln;end. ---------------------------------------------------------------------- maurobio · 2019-11-05 10:46 Dear ALL, I have strings with some embedded characters as follows: s := '\i{}This is a string\i0{}' I want to get rid of the \{} and \i0{} using the following regex: e := '\\i\d*{}' But when I attempt this: n := ReplaceRegExpr(e, s, '', True); I get the error: TRegrExpr(comp): Nested *?+ (pos 6) The above regex works in Python (as n = re.sub(e, '', s)), but obviously the FPC implementation is different. Could someone give me a hand? Thanks in advance! Best regards, ---------------------------------------------------------------------- Thaddy · 2019-11-05 10:51 I already replied in the other post: use an escape to escape a slash \\ ---------------------------------------------------------------------- maurobio · 2019-11-05 11:11 Thanks, @Thaddy! ---------------------------------------------------------------------- maurobio · 2019-11-05 11:18 ... but when I changed the regex to: e := '\\i\\d*{}' I still get the same error (just one position changed): TRegrExpr(comp): Nested *?+ (pos 7) Any hints? ---------------------------------------------------------------------- bytebites · 2019-11-05 12:44 e:='\\i\d*\{}' () is not needed. ---------------------------------------------------------------------- maurobio · 2019-11-05 20:03 @bitebyes: There are no parentheses ('()') in my regex, those are braces ('{}'), which are part of the string and that I want to get rid of. Cheers,