簡體   English   中英

使用Java Regex匹配字母字符,且不帶百分號

[英]Matching alphabetical characters with Java Regex which are not preceded by percent sign

tl; dr:

我想要一個像這樣的字符串: ab%cde%fg hij %klm n%op

並將其轉換為任何(都可以接受):

  • 'ab'%c'de'%f'g hij '%k'lm n'%o'p'
  • 'ab'%c'de'%f'g' 'hij' %k'lm' 'n'%o'p'
  • 'a''b'%c'd''e'%f'g' 'h''i''j' %k'l''m' 'n'%o'p'

(如果字母字符前沒有% ,則必須將其放在單引號內。可以使用開和閉多余的單引號)

用例

我正在嘗試采用C strftime格式的字符串,並將其轉換為可與Java的SimpleDateFormat 在大多數情況下,這很簡單:

String format = "%y-%m-%d %H:%M:%S";

Map<String, String> replacements = new HashMap<String, String>() {{
    put("%a", "EEE");
    put("%A", "EEEE");
    put("%b", "MMM");
    put("%B", "MMMM");
    put("%c", "EEE MMM dd HH:mm:ss yyyy");
    // ... for each strftime token, create a mapping ...
}};

for ( String key : replacements.keySet() )
{
    // apply the mappings one at a time
    format = format.replaceAll(key, replacements.get(key));
}

// Then format
SimpleDateFormat df = new SimpleDateFormat(format, Locale.getDefault());
System.out.println(df.format(Calendar.getInstance().getTime()));

但是,當我介紹字符文字時,就會遇到問題。 根據strftime文檔, 所有不帶百分號的字符文字都將傳遞而不修改輸出字符串。 所以:

Format: "%y is a great year!"
Output: "2019 is a great year!"

但是,對於SimpleDateFormat ,所有字符文字均被視為標記,除非用單引號引起來:

Format: "yyyy 'is a great year!'"
Output: "2019 is a great year!"

Format: "yyyy is a great year!"
Output: ERROR - invalid token "i"

期望的輸出

由於strftime令牌始終是單個字符 ,因此修復我們的格式字符串應該不會太困難。 在最壞的情況下,“如果一個字母不是由前面%的標志,把它包在單引號”,這將導致:

Format: "%y is a great year!"
Processed: "%y 'i''s' 'a' 'g''r''e''a''t' 'y''e''a''r'!"

這很丑陋,但會達到預期效果,並且是可以接受的答案。 理想情況下,我們將包裝所有不包含%的字母字符 ,如下所示:

Format: "%y is a great year!"
Processed: "%y 'is' 'a' 'great' 'year'!"

或者更好的是,所有運行都包括非字母和非%字符

Format: "%y is a great year!"
Processed: "%y' is a great year!'"

我嘗試過的

我從一個毫無頭腦的正則表達式開始,我很確定那是行不通的,並且沒有:

format.replaceAll("[^%]([a-zA-Z]+)", "'$1'");
// Format:   "Literal %t Literal"
// Output:   "'iteral' %t'Literal'"
// Expected: "'Literal' %t 'Literal'"

我對后向引用沒有足夠的了解,所以我給了他們一個旋轉,但也弄亂了那里的東西:

format.replaceAll("(?!%)([a-zA-Z]+)", "'$1'");
// Format:   "Literal %t Literal"
// Output:   "'Literal' %'t' 'Literal'"
// Expected: "'Literal' %t 'Literal'"

我還考慮過編寫一個非常簡單的詞法分析器。 就像是:

StringBuffer s = new StringBuffer();
boolean inQuote = false;
for (int i = 0; i < format.length; i++)
{
    if (format[i] == '%')
    {
        i++;
        s.append(replacements.get(format[i]);
    }
    else if (inQuote)
    {
        s.append(format[i]);
    }
    else
    {
        s.append("'");
        inQuote = true;
        s.append(format[i]);
    }
}

但是,我了解到format[i]不是有效的Java語法,並且在我決定只在此處發表文章之前,沒有花太多時間研究如何從字符串中正確獲取字符。

我希望使用正則表達式解決方案,以便可以將其寫在一行中,而不是像這樣的循環。

已更新為可使用單個正則表達式。 可以添加其他格式以測試正確性。

      String[] formats = { "ab%cde%fg hij %klm n%op", "ab%c", "%d"
      };
      for (String f : formats) {
         String parsed = f.replaceAll("(^[a-z]+|(?<=%[a-z])([a-z ]+))", "'$1'");
         System.out.println(parsed);
      }

兩種可能性是:

  • 將在%[az]所有字符[az]+放在單引號之間。
  • 將所有%之前且以上未包括的字符放在單引號之間。

既然已經考慮過,為什么不使用幾個replaceAll函數呢?

首先,對所有連續的字符串添加單引號;

然后,將單引號前跟%移一個字符;

最后,刪除空引號。

以下是我在Python中的測試代碼。 我相信它也可以在其他語言(例如Java)中使用。

>>> str1=re.sub("([a-zA-Z]+)","'\g<1>'",input)
>>> str2=re.sub("%'([a-zA-Z])'","%\g<1>",str1)
>>> str3=re.sub("''","",str2)
>>> str1
"'Literal' %'t' 'Literal'"
>>> str2
"'Literal' %t 'Literal'"
>>> str3
"'Literal' %t 'Literal'"

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM