如何在正则表达式中跳过引用文本(或如何使用HyperStr ParseWord处理Unicode文本?)

5
我需要正则表达式帮助,创建一个delphi函数来替代Rad Studio XE2中的HyperString ParseWord函数。 HyperString是一个非常有用的字符串库,但它从未跨越到Unicode。我已经基本上完成了它,但它根本不尊重引号定界符。我需要它完全匹配下面描述的函数:
function ParseWord(const Source,Table:String;var Index:Integer):String; 顺序从左到右使用单个字符分隔符表进行标记解析。在引号字符串中忽略定界符。表中不允许使用引号定界符。 索引是一个指针(初始化为“1”表示第一个单词)由该函数更新以指向下一个单词。要检索下一个单词,只需使用先前返回的Index值再次调用函数即可。 注意:如果Length(Resultant)= 0,则没有其他单词可用。定界符在引号字符串中被忽略。(我的强调)
这是截至目前为止的内容:
function ParseWord( const Source, Table: String; var Index: Integer):string;
var
  RE : TRegEx;
  match : TMatch;
  Table2,
  chars : string;
begin
  if index = length(Source) then
  begin
    result:= '';
    exit;
  end;

  // escape the special characters and wrap in a Group
  Table2 :='['+TRegEx.Escape(Table, false)+']';
  RE := TRegEx.create(Table2);
  match := RE.Match(Source,Index);
  if match.success then
  begin
    result := copy( Source, Index, match.Index - Index);
    Index := match.Index+match.Length;
  end
  else
  begin
    result := copy(Source, Index, length(Source)-Index+1);
    Index := length(Source);
  end;
end;

  while ( Length(result)= 0) and (Index<length(Source)) do
  begin
    Inc(Index);
    result := ParseWord(Source,Table, Index);
  end;

干杯并感谢。

2个回答

1
我会尝试在Table2中使用这个正则表达式:
Table2 := '''[^'']+''|"[^"]+"|[^' + TRegEx.Escape(Table, false) + ']+';

演示:
这个演示更像是一个POC,因为我无法找到一个在线Delphi正则表达式测试器。

  • 分隔符是空格(ASCII码32)和管道(ASCII码124)字符。
  • 测试句子为:

toto titi "alloa toutou" 'dfg erre' 1245|coucou "nestor|delphi" "" '' http://regexr.com?32i81

讨论:
我假设引用字符串是由两个单引号(')或两个双引号(")括起的字符串。如果我错了,请纠正我。

正则表达式将匹配以下内容之一:

  • 单引号字符串
  • 双引号字符串
  • 不由任何传递的分隔符组成的字符串

已知缺陷:
由于我不知道ParseWord如何处理字符串内部的引号转义,因此该正则表达式不支持此功能。

例如:

  • 如何解释这个 'foo''bar' ? => 两个标记: 'foo''bar' 或一个单一的标记 'foo''bar'
  • 那么这种情况呢:"foo""bar" ? => 两个标记: "foo""bar" 或一个单一的标记 "foo""bar"

这个代码并没有按照给定的方式工作,但是它让我找到了解决方案。我会将完整的解决方案单独列出,但所有的功劳归于你。 - marcp

0
在我的原始代码中,我正在查找定界符并将其前面的所有内容作为我的下一个匹配项,但是当在引号中查找某些内容时,这个概念没有得到延续。@Stephan的建议最终导致我找到了一些可行的方法。一个我之前没有提到的额外复杂性是HyperStr可以使用任何字符作为引号字符。默认是双引号,但您可以使用函数调用更改它。
在我的解决方案中,我已经明确地硬编码了QuoteChar为双引号,这适合我的目的,但是在另一个函数中将QuoteChar设置为全局变量并进行设置将是微不足道的。我还成功地使用单引号(ascii 39)进行了测试,在Delphi中这将是棘手的一个。
function ParseWord( const Source, Table: String; var Index: Integer):string;
var
  RE : TRegEx;
  match : TMatch;
  Table2: string;
  Source2 : string;
  QuoteChar : string;
begin
  if index = length(Source) then
  begin
    result:= '';
    exit;
  end;

  // escape the special characters and wrap in a Group
  QuoteChar := #39;
  Table2 :='[^'+TRegEx.Escape(Table, false)+QuoteChar+']*|'+QuoteChar+'.*?'+QuoteChar ;
  Source2 := copy(Source, Index, length(Source)-index+1);
  match := TRegEx.Match(Source2,Table2);
  if match.success then
  begin
    result := copy( Source2, match.index, match.length);
    Index := Index + match.Index + match.Length-1;
  end
  else
  begin
    result := copy(Source, Index, length(Source)-Index+1);
    Index := length(Source);
  end;
  while ( Length(result)= 0) and (Index<length(Source)) do
  begin
    Inc(Index);
    result := ParseWord(Source,Table, Index);
  end;

end;

这个解决方案没有从引用字符串中去掉引号字符,但是我无法从自己现有的代码中判断它是否应该这样做,也无法使用Hyperstr进行测试。也许其他人知道吗?


我不确定关于SO协议应该把答案给谁。我把解决方案作为一个单独的答案添加,因为我无法将其格式化为@Stephan的评论,但他确实引导了我找到答案。 - marcp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接