![](/img/trans.png)
[英]Java & Regex: Matching a substring that is not preceded by specific characters
[英]Matching alphabetical characters with Java Regex which are not preceded by percent sign
我想要一個像這樣的字符串: 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.