正则表达式-不能将西里尔字母与\ w匹配

时间:2019-10-03 20:00:25

标签: java regex

任务:

  

必须使用正则表达式而不使用容器类来解决任务。

     

输入:文本(可能包含拉丁语和西里尔字母)。输出:源文本,但是每个单词的第一个字符(包含三个或更多字符)的大小写必须颠倒。

     

将单词视为仅包含字母的序列(单词中不包含所有其他字符)。创建一个将输入转换为输出的静态转换方法。

     

示例输入数据

When I was younger
I never needed
Прощай, со всех вокзалов поезда
уходят в Дальние Края
     

示例输出

when I Was Younger
I Never Needed
прощай, со Всех Вокзалов Поезда
Уходят в дальние края

我的尝试

public static String convert(String input) {
    StringBuilder sb = new StringBuilder(input);
    Pattern p = Pattern.compile("[\\W&&[\\d]]?[\\w&&[\\D]]+");
    Matcher m = p.matcher(input);
    while (m.find()) {
        if (m.group().length() >= 3) {
            if (Character.isUpperCase(sb.charAt(m.start()))) {
                sb.setCharAt(m.start(), Character.toLowerCase(sb.charAt(m.start())));
            } else {
                sb.setCharAt(m.start(), Character.toUpperCase(sb.charAt(m.start())));
            }

        }
    }
    return sb.toString();
}

我需要输出:

when I Was Younger
I Never Needed
прощай, со Всех Вокзалов Поезда
Уходят в дальние края

但是我有:

when I Was Younger
I Never Needed
Прощай, со всех вокзалов поезда
уходят в Дальние Края

3 个答案:

答案 0 :(得分:1)

调试问题

\w与西里尔字符不匹配。我通过在您的while循环中打印匹配的组来解决此问题:

System.out.println(m.group());

打印:

  

何时   
我   
是   
年轻人   
我   
从来没有   
需要

没有其他单词匹配。

解决方案1 ​​

要匹配西里尔字母,也可以使用\p{L}。如果使用{3}来匹配三个字符,则可以避免在循环中进行长度检查。 \b与边界字符匹配。全部放在一起:

public static String convert(String input) {
    StringBuilder sb = new StringBuilder(input);
    Pattern p = Pattern.compile("\\b\\p{L}{3}");
    Matcher m = p.matcher(input);
    while (m.find()) {
        char firstChar = sb.charAt(m.start());
        if (Character.isUpperCase(firstChar)) {
            sb.setCharAt(m.start(), Character.toLowerCase(firstChar));
        } else {
            sb.setCharAt(m.start(), Character.toUpperCase(firstChar));
        }
    }
    return sb.toString();
}

产生:

  

我年轻的时候   
我从不需要   
прощай,соВсехВокзаловПоезда   
Уходятвдальниекрая

解决方案2

或者,如果您想真正地呆住,请使用正向超前(非捕获组)和采用lambda的匹配器replaceAll方法:

public static String convert(String input) {
    Pattern p = Pattern.compile("\\b(\\p{L})(?=\\p{L}{2})");
    Matcher m = p.matcher(input);
    return m.replaceAll(match -> {
        char ch = match.group().charAt(0);
        if (Character.isUpperCase(ch)) {
            return "" + Character.toLowerCase(ch);
        }
        return "" + Character.toUpperCase(ch);
    });
}

还产生:

  

我年轻的时候   
我从不需要   
прощай,соВсехВокзаловПоезда   
Уходятвдальниекрая

答案 1 :(得分:0)

if (Character.isUpperCase(sb.charAt(m.start()))) {
    sb.setCharAt(m.start(), Character.toLowerCase(sb.charAt(m.start())));
} else {
    sb.setCharAt(m.start(), Character.toUpperCase(sb.charAt(m.start())));
}

您需要调试这段代码。一种方法是通过将中间结果分配给它们自己的变量来打破复杂性并减少重复:

char firstChar = sb.charAt(m.start());
if (Character.isUpperCase(firstChar)) {
    char lowerChar = Character.toLowerCase(firstChar);
    sb.setCharAt(m.start(), lowerChar);
} else {
    char upperChar = Character.toUpperCase(firstChar);
    sb.setCharAt(m.start(), upperChar);
}

现在您可以打印出这些变量的值以进行检查。

答案 2 :(得分:0)

主要问题是默认情况下\w仅接受英语字母。要使其与其他语言的字母字符匹配,您需要添加Pattern.UNICODE_CHARACTER_CLASS标志:

Pattern p = Pattern.compile("[\\W&&[\\d]]?[\\w&&[\\D]]+", Pattern.UNICODE_CHARACTER_CLASS);
//                                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

另外,您的模式看起来也不正确(或者因为它背后的主要思想不清楚,所以根本无法读取)。由于在此示例中,您只想修改至少包含3个字符的单词,因此可以将其描述为

Pattern p = Pattern.compile("(\\p{Alpha})(\\p{Alpha}{2,})", Pattern.UNICODE_CHARACTER_CLASS);

其中

  • 在第1组中,我们将存储第一个字符
  • 在第2组中,我们将存储其余字符。

这样,您的代码可能如下所示:

public static String convert(String input) {
    Pattern p = Pattern.compile("(\\p{Alpha})(\\p{Alpha}{2,})", Pattern.UNICODE_CHARACTER_CLASS);
    Matcher m = p.matcher(input);
    return m.replaceAll(match -> {
        char firstChar = match.group(1).charAt(0);
        if (Character.isUpperCase(firstChar)) {
            return Character.toLowerCase(firstChar) + match.group(2);
        } else {
            return Character.toUpperCase(firstChar) + match.group(2);
        }
    });
}