JAVA

индекс
157,35

Java-головоломка: Капитализация слов в строке за одно java-выражение

Представляю вашему вниманию небольшую головоломку по Java.
Это реальная задача, возникшая передо мной и коллегами в процессе разработки и имеет вполне оправданное применение.

Итак, условие:
Необходимо реализовать ОДНИМ выражением Java (конечно же, используя только стандартные библиотеки) задачу по капитализации слов произвольной строки.

То есть, что-то вроде этого:
String strOrig = "строка с большим количеством слов";
String strRes = <.. тут какое-то выражение ..>;
assert strRes.equals("Строка С Большим Количеством Слов");

Наше решение головоломки оказалось очень интересным и я решил предоставить сообществу возможность предложить свои пути решения данной задачи.
Жду ваших предположений!

Результаты мозговой деятельности под катом

Мистер alex14n и мистер Daymz предложили решение с использованием анонимных классов, вполне удовлетворяющее условию задачи:
String strRes = (new Object() {
public String work(String string) {
/* а тут много кода */
}
}).work(strOrig);


Ну и красивейшее решение (аналогичное нашему) предложил мистер trg.
Чуть раньше него, в асю стукнул человек с ником Blazkowicz, предложив такой же способ:
String strRes = String.format(strOrig.replaceAll("\\b(\\S)", "%S"), (Object[]) strOrig.replaceAll("\\b(\\S)\\S*", "$1").split("\\s+"));

Еще одно решение с использованием только риплейсов предложил Daymz:
String strRes = (" " + strOrig)
.replaceAll(
"\\s\\S+",
"$0 " + strOrig
.replaceAll("\\s", "")
.replaceAll("\\\\", "\\\\\\\\")
.replaceAll("\\$", "\\\\\\$")
.toUpperCase()
)
.replaceAll("(?i)(\\s)(\\S)(\\S*) \\S*(\\2)(\\S*)", "$1$4$3")
.substring(1);


Спасибо всем за проявленный интерес!
+4
15 октября 2009, 13:10
2

комментарии (41)

+5
SkywalkerY #
интересно, почему так важно было использовать только ОДНУ строку?
str = org.apache.commons.lang.StringUtils.capitalize(str);
+1
433t #
apache-commons это не стандартная библиотека. Читайте условие внимательнее!
Необходимость формировать такое в одну строку, может быть, например, для преобразования значений в отчетах Jasper (это было в нашем случае).
0
opeg #
String strRes = strOrig.toUpperCase();

или я чего-то не понял?
+1
433t #
не поняли) в данном случае ВСЕ буквы станут заглавными:
«СТРОКА С БОЛЬШИМ КОЛИЧЕСТВОМ СЛОВ»
0
3aBTpaK #
toUpperCase( strOrig.charAt(0) ) + strOrig.substring(1)
+1
433t #
Это «поднимет» только первый символ строки ;)
+4
iv_s #
 String strRes = strOrig.replaceAll("\\b(\\S)", "$1");
Осталось толькл придумать, как из этого самого $1 получить строку, чтоб ее toUpperCase:)
Правдо это только для латиницы…
+1
433t #
В правильном направлении движетесь, товарищ!

P.S. вышеуказанный риплейс сработает не только для латиницы…
+1
serf #
Я тоже так думал
String s = «строка с большим количеством слов»;
String ss = s.replaceAll("(\\s)([а-я])", "$1>$2");
но модификатор > (знак больше) не работает, но суть такая
+2
remal #
strRes = strOrig.replaceAll(
"\\b(\\w)",
"$1:" + strOrig.replaceAll("[^\\w]", "").toUpperCase() +":"
).replaceAll(
"\\b(\\w):.*(\\1).*:",
"$2"
);

Осталось во второй замене отключить соответствие регистра. Сделать это можно через Pattern и Matcher
+1
serf #
Интересно, но мне почему-то показалось что под «ОДНИМ выражением» понимается вызовом одного метода один раз.
+3
remal #
Одно присваивание — одно выражение…
+1
433t #
Попробовал Ваш вариант — что-то не совсем работает…
Можете представить полность доработанный вариант?

PS: В нашем варианте используется не только комбинация replaceAll().
–1
Stark #
inputWord.substring(0,1).toUpperCase() + inputWord.substring(1);
+1
433t #
Ещё раз напоминаю, что речь идёт не об отдельном слове, а строке, содержащей НЕСКОЛЬКО слов. А Ваш пример даже не обработает случая строки, начинающейся с вайтспейса )
–1
lashtal #
Сорри что лезу в чужой монастырь со своим PHP, но вот решение на нем:
$strRes = mb_ereg_replace("([a-zA-Zа-яА-Я])(\w*.*?)",'mb_strtoupper(\'\\1\')."\\2"',$strOrig,'e');

Думаю, в яве можно сделать аналогичным образом.
+2
433t #
у меня в первую очередь возникло решение на perl, вида:
$str = ~s/\b(\w)/\U$1/g;
но увы в Джавовских регекспах нету препроцессинговых модификаторов…
0
xtender #
Ага, тоже сразу подумал также:
$str=~s/\b\w/\U$&/g;

Решение на Java я смотрю аналогично.
+2
alex14n #
пока думаю, мысль вслух: за Scala — будущее:
val strRes = strOrig split "\\b" filter {_.length > 0} map {x => x.charAt(0).toUpperCase + x.substring(1)} mkString
+1
alex14n #
точнее даже strOrig split "\\b" map {_.capitalize} mkString
+1
thevery #
практически аналогично на groovy:
strOrig.split("\b").collect{it.toUpperCase()}.join()
+2
alex14n #
В одно выражение:

String strRes = (new java.util.StringTokenizer(strOrig, " ", true) {
    public String toString() {
        StringBuilder sb = new StringBuilder();
        while (hasMoreTokens()) {
            String token = nextToken();
            sb.append (Character.toUpperCase(token.charAt(0)));
            sb.append (token.substring(1));
        }
        return sb.toString();
    }
}).toString();
+1
433t #
не могу не согласиться. Это вполне заслуживающее уважения решение.
Но есть другое :)

PS: мы пробовали сделать так в первую очередь, но JasperReport не схвал это — в муках было рождено симпатичное решение без анонимных классов =)
PPS: один товарищ, у которого нету акка на хабре прислал абсолютно верное решение. Предлагаю еще немного подумать!
+2
Daymz #
Что для Вас «одно выражение»?

String strRes = (new Object() {
  public String work(String string) {
    /* а тут много кода */
  }
}).work(strOrig);

— это одно выражение? Если нет, то почему?
+1
Daymz #
alex14n меня опередил :)
+1
433t #
все ок. выражение оно и в Африке выражение) но, предлагаю подумать над альтернативным решением!
+3
Daymz #
Вариант, не требующий экзотических символов и корректно обрабатывающий знак доллара:

String strRes = (" " + strOrig)
    .replaceAll(
        "\\s\\S+", 
        "$0 " + strOrig
                .replaceAll("\\s", "")
                .replaceAll("\\$", "\\\\\\$")
                .toUpperCase()
    )
    .replaceAll("(?i)(\\s)(\\S)(\\S*)\\s+\\S*(\\2)(\\S*)", "$1$4$3")
    .substring(1);
+1
Daymz #
В самом страшном регэкспе можно чуть проще:

"(?i)(\\s)(\\S)(\\S*) \\S*(\\2)(\\S*)"
+1
Daymz #
Добавил обработку бэкслэша:

String strRes = (" " + strOrig)
    .replaceAll(
        "\\s\\S+", 
        "$0 " + strOrig
                .replaceAll("\\s", "")
                .replaceAll("\\\\", "\\\\\\\\")
                .replaceAll("\\$", "\\\\\\$")
                .toUpperCase()
    )
    .replaceAll("(?i)(\\s)(\\S)(\\S*) \\S*(\\2)(\\S*)", "$1$4$3")
    .substring(1);
+2
trg #
String strRes = String.format(strOrig.replaceAll("\\b(\\S)", "%S"), (Object[]) strOrig.replaceAll("\\b(\\S)\\S*", "$1").split("\\s"));

+1
trg #
>> .split("\\s")
-> .split("\\s+")

ещё надо что то сделать если в оригинальной строке есть символы '%' :D можно заменить на какой то заведомо не используемый, а потом обратно
+1
Daymz #
Ещё строка «this is a $5 string» превращается в «This Is A $$5 String».
+1
433t #
ну это все мелочи! :)
+1
Daymz #
Конечно, всего лишь $5 :)
+1
433t #
именно так =) на мой взгляд интересное элегантное решение!
+1
max7 #
Я не силён в java, поэтому вот javascript:
("строка с большим количеством слов").replace(/(^.|\s.)/g, function(_0, _1) { return _1.toUpperCase();})
+1
max7 #
* /(^[\S]|[\s]{1,}[\S])/gm
+1
Egiptyanin #
brainfuck?
+1
max7 #
* strOrig.replace(/(^\S|\s+\S)/gm, function(_0, _1) { return _1.toUpperCase();}) (чего то со мной не то сегодня)
–2
pingu #
На пайтоне:
>>> a = «stroka s bolshim kolvom simvolov»
>>> " ".join([word[0].upper()+word[1:] for word in a.split(" ")])
'Stroka S Bolshim Kolvom Simvolov'
Возможно в жабке можно сделать что-то подобное
0
Throwable #
А почему так не попробовать?
String strRes = «Строка С Большим Количеством Слов»;

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.