Сопоставление букв алфавита с Java Regex, которому не предшествует знак процента - PullRequest
1 голос
/ 16 мая 2019

ТЛ; др:

Я хочу взять строку вроде: 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, и не потратил много времени на изучение того, как правильно получить символ из строки, прежде чем я решил просто опубликовать здесь.

Я бы предпочел решение с регулярным выражением, чтобы я мог написать его в одной строке вместо цикла, подобного этому.

Ответы [ 2 ]

1 голос
/ 17 мая 2019

Почему бы не использовать несколько функций 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'"
1 голос
/ 16 мая 2019

Это было обновлено для работы с одним регулярным выражением. Для проверки правильности можно добавить дополнительные форматы.

      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);
      }

Две возможности:

  • Поместите все символы [a-z]+, следующие за %[a-z], в одинарные кавычки.
  • Поместите любые символы, которые предшествуют % и не включены выше, между одинарные кавычки.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...