【问题标题】:Remove elements from Date Format String using a Regular Expression使用正则表达式从日期格式字符串中删除元素
【发布时间】:2019-12-21 00:47:40
【问题描述】:

我想删除提供的日期格式字符串中的元素 - 例如,通过删除任何非 M/y 元素将格式“dd/MM/yyyy”转换为“MM/yyyy”。

我要做的是根据为区域设置提供的现有日/月/年格式创建本地化的月/年格式。

我已经使用正则表达式完成了这项工作,但解决方案似乎比我预期的要长。

一个例子如下:

public static void main(final String[] args) {
 System.out.println(filterDateFormat("dd/MM/yyyy HH:mm:ss", 'M', 'y'));
 System.out.println(filterDateFormat("MM/yyyy/dd", 'M', 'y'));
 System.out.println(filterDateFormat("yyyy-MMM-dd", 'M', 'y'));
}

/**
 * Removes {@code charsToRetain} from {@code format}, including any redundant
 * separators.
 */
private static String filterDateFormat(final String format, final char...charsToRetain) {
 // Match e.g. "ddd-"
 final Pattern pattern = Pattern.compile("[" + new String(charsToRetain) + "]+\\p{Punct}?");
 final Matcher matcher = pattern.matcher(format);

 final StringBuilder builder = new StringBuilder();

 while (matcher.find()) {
  // Append each match
  builder.append(matcher.group());
 }

 // If the last match is "mmm-", remove the trailing punctuation symbol
 return builder.toString().replaceFirst("\\p{Punct}$", "");
}

【问题讨论】:

  • 您希望解决方案能持续多久?它必须有多灵活,即是否允许像 "MM/yy - MM" 这样的“疯狂”格式?
  • @Thomas 好问题!如果它这样做会很棒 - 但我认为它不太可能需要。
  • 我不认为有很多 shorter 方法可以做到这一点。有很多方法可以解决这个问题,但我很确定在 2 行左右的代码中没有“干净”的方法。具体问题需要具体的解决方案,而这些解决方案很少漂亮。
  • @Link19 如果您替换第一部分或最后一部分,这将不起作用。例如。在替换dd/MM/yyyy 中的日期后,不会创建重复的/。这也将删除日期格式所需的重复字符,从而产生/M/y
  • @Jakg 解决方案很短,解释很长:)

标签: java regex date-formatting


【解决方案1】:

我将尝试在理解我的问题的情况下回答:如何从字符串的列表/表/数组中删除不完全遵循模式“dd/MM”的元素。

所以我正在寻找一个看起来像

的函数
public List<String> removeUnWantedDateFormat(List<String> input)

根据我对 Dateformat 的了解,我们可以预期只有 4 种可能是您想要的,希望我不会错过任何一种,它们是“MM/yyyy”、“MMM/yyyy”、“MM/yy”、“MM/ yyyy”。为了让我们知道我们在寻找什么,我们可以做一个简单的函数。

public List<String> removeUnWantedDateFormat(List<String> input) {
  String s1 = "MM/yyyy";
  string s2 = "MMM/yyyy";
  String s3 = "MM/yy";
  string s4 = "MMM/yy";

  for (String format:input) {
    if (!s1.equals(format) && s2.equals(format) && s3.equals(format) && s4.equals(format))
      input.remove(format);
  }
  return input;
}

如果可以,最好不要使用正则表达式,它会消耗大量资源。很大的改进是使用您接受的日期格式的枚举,这样您可以更好地控制它,甚至替换它们。

希望这会有所帮助,干杯

编辑:在我看到评论后,我认为最好使用包含而不是等于,应该像魅力一样工作而不是删除,

输入 = 应为字符串。

所以看起来更像:

public List<String> removeUnWantedDateFormat(List<String> input) {
  List<String> comparaisons = new ArrayList<>();
  comparaison.add("MMM/yyyy");
  comparaison.add("MMM/yy");
  comparaison.add("MM/yyyy");
  comparaison.add("MM/yy");

  for (String format:input) {
    for(String comparaison: comparaisons)
      if (format.contains(comparaison)) {
      format = comparaison;
      break;
    }
  }
  return input;
}

【讨论】:

  • 很抱歉,我不明白这如何回答我的问题?
  • 我正在尝试动态执行此操作,而不是硬编码任何内容。
  • 你能举个例子说明这实际上是做什么的吗?
  • mhh,是的,那么我想我的解决方案是一个解决方案,但不是您的问题。我终于明白了真正的问题,对不起。我以你的例子为问题,所以我的回答是针对一个用例的。但我想你不需要帮助
【解决方案2】:

让我们尝试以下日期格式字符串的解决方案:

String[] formatStrings = { "dd/MM/yyyy HH:mm:ss", 
                           "MM/yyyy/dd", 
                           "yyyy-MMM-dd", 
                           "MM/yy - yy/dd", 
                           "yyabbadabbadooMM" };

下面将分析匹配的字符串,然后打印匹配的第一组。

Pattern p = Pattern.compile(REGEX);
for(String formatStr : formatStrings) {
    Matcher m = p.matcher(formatStr);
    if(m.matches()) {
        System.out.println(m.group(1));
    }
    else {
        System.out.println("Didn't match!");
    }
}

现在,我尝试了两个独立的正则表达式。第一:

final String REGEX = "(?:[^My]*)([My]+[^\\w]*[My]+)(?:[^My]*)";

有程序输出:

MM/yyyy
月/年
yyyy-MMM
不匹配!
不匹配!

第二:

final String REGEX = "(?:[^My]*)((?:[My]+[^\\w]*)+[My]+)(?:[^My]*)";

有程序输出:

MM/yyyy
月/年
yyyy-MMM
月/年 - 年
不匹配!

现在,让我们看看第一个正则表达式实际匹配的是什么:

(?:[^My]*)([My]+[^\\w]*[My]+)(?:[^My]*) First regex =
(?:[^My]*)                              Any amount of non-Ms and non-ys (non-capturing)
          ([My]+                        followed by one or more Ms and ys
                [^\\w]*                 optionally separated by non-word characters
                                        (implying they are also not Ms or ys)
                       [My]+)           followed by one or more Ms and ys
                             (?:[^My]*) finished by any number of non-Ms and non-ys
                                        (non-capturing)

这意味着至少需要 2 M/ys 才能匹配正则表达式,尽管您应该注意 MM-dd 或 yy-DD 之类的东西也会匹配,因为它们有两个 M-or-y区域 1 个字符长。您可以通过对日期格式字符串进行完整性检查来避免在这里遇到麻烦,例如:

if(formatStr.contains('y') && formatStr.contains('M') && m.matches())
{
    String yMString = m.group(1);
    ... // other logic
}

至于第二个正则表达式,它的意思是:

(?:[^My]*)((?:[My]+[^\\w]*)+[My]+)(?:[^My]*) Second regex =
(?:[^My]*)                                   Any amount of non-Ms and non-ys 
                                             (non-capturing)
          (                      )           followed by
           (?:[My]+       )+[My]+            at least two text segments consisting of
                                             one or more Ms or ys, where each segment is
                   [^\\w]*                   optionally separated by non-word characters
                                  (?:[^My]*) finished by any number of non-Ms and non-ys
                                             (non-capturing)

此正则表达式将匹配稍宽的字符串系列,但仍要求 Ms 和 ys 之间的任何分隔符都是非单词 ([^a-zA-Z_0-9])。此外,请记住,此正则表达式仍将匹配“yy”、“MM”或类似的字符串,如“yyy”、“yyyy”...,因此按照前一个常规的描述进行健全性检查会很有用表达。

此外,这里有一个快速示例,说明如何使用上述方法来操作单个日期格式字符串:

LocalDateTime date = LocalDateTime.now();
String dateFormatString = "dd/MM/yyyy H:m:s";
System.out.println("Old Format: \"" + dateFormatString + "\" = " + 
    date.format(DateTimeFormatter.ofPattern(dateFormatString)));
Pattern p = Pattern.compile("(?:[^My]*)([My]+[^\\w]*[My]+)(?:[^My]*)");
Matcher m = p.matcher(dateFormatString);
if(dateFormatString.contains("y") && dateFormatString.contains("M") && m.matches())
{
    dateFormatString = m.group(1);
    System.out.println("New Format: \"" + dateFormatString + "\" = " + 
        date.format(DateTimeFormatter.ofPattern(dateFormatString)));
}
else
{
    throw new IllegalArgumentException("Couldn't shorten date format string!");
}

输出:

旧格式:“dd/MM/yyyy H:m:s” = 14/08/2019 16:55:45
新格式:“MM/yyyy”= 08/2019

【讨论】:

  • 你能发布一个实际使用的例子吗?我很难想象你会如何使用它。
  • 谢谢!这很好用并且避免了循环(至少对于正则表达式)。您可以轻松地将字符注入正则表达式以对其进行参数化。例如。 MessageFormat.format("(?:[^{0}]*)([{0}]+[^\\w]*[{0}]+)(?:[^{0}]*)", new String(charsToRetain))
  • @Jakg 如果你在中间允许单词字符,你会遇到很多讨厌的匹配,比如yyabbadabbadooMM。如果您确实希望任何中间字符匹配,您只需将正则表达式中的[^\\w] 更改为[^My]
  • @Jakg 如果您使用y 的替换,您必须意识到([y]+[^y]*[y]+) 将需要至少2 个ys,可选用非ys 分隔。如果您希望第二个y 组是可选的,您只需将整个替换表达式更改为:MessageFormat.format("(?:[^{0}]*)([{0}]+[^\\w]*[{0}]?)(?:[^{0}]*)", new String(charsToRetain)) 即可。 [^\\w]* 捕获第 1 组右侧的每个非单词字符可能是个问题,所以不要勉强:MessageFormat.format("(?:[^{0}]*)([{0}]+[^\\w]*?[{0}]?)(?:[^{0}]*)", new String(charsToRetain))
  • 哦,如果您将 [^\\w] 更改为 [^{0}],就像您所做的那样,您几乎肯定不得不让捕获不情愿。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-20
  • 1970-01-01
  • 2013-05-23
  • 1970-01-01
  • 1970-01-01
  • 2021-01-17
  • 2014-06-20
相关资源
最近更新 更多