ТЛ; др:
Я хочу взять строку вроде: 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
формате и преобразовать ее для работы с SimpleDateFormat
в Java. По большей части это довольно просто:
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, и не потратил много времени на изучение того, как правильно получить символ из строки, прежде чем я решил просто опубликовать здесь.
Я бы предпочел решение с регулярным выражением, чтобы я мог написать его в одной строке вместо цикла, подобного этому.