Верхняя фамилия, за исключением строчной префиксной секции фамилии
Я пытаюсь определить метод для прописной фамилии; однако, исключая нижний префикс.
Пример имен и их преобразование:
- MacArthur → MacARTHUR
- McDavid → McDAVID
- LeBlanc → LeBLANC
- Макинтайр → McINTYRE
- de Wit → de WIT
Есть также имена, которые будут содержать фамилии, которые должны быть полностью заглавные, поэтому простой функции для идентификации префикса, такого как strchr()
, будет недостаточно:
- Macmaster → MACMASTER
- Macintosh → MACINTOSH
Функция PHP mb_strtoupper()
не подходит, так как она набирает всю строку. Точно так же strtoupper()
не подходит, а также теряет акценты на акцентированных именах.
Есть несколько ответов вокруг SO, которые частично отвечают на вопрос, например: Капитализация с использованием PHP
Однако общий дефицит предполагает, что все имена с фамилией как Mac
сопровождаются капиталом.
Имена правильно заглавные буквы в базе данных, поэтому мы можем предположить, что имя написано как Macarthur правильно, или MacArthur является правильным для другого человека.
Ответы
Ответ 1
Следуя правилу, чтобы загладить все после прописной буквы last:
preg_replace_callback('/\p{Lu}\p{Ll}+$/u',
function ($m) { return mb_strtoupper($m[0]); },
$name)
\p{Lu}
и \p{Ll}
являются символами верхнего и нижнего регистра Юникода соответственно, а mb_strtoupper
- unicode известно... для простого варианта ASCII-варианта это тоже сделало бы:
preg_replace_callback('/[A-Z][a-z]+$/',
function ($m) { return strtoupper($m[0]); },
$name)
Ответ 2
Я считаю, что это решение вопроса:
$names = array(
'MacArthur',
'Macarthur',
'ÜtaTest',
'de Wit'
);
$pattern = '~(?<prefix>(?:\p{Lu}.+|.+\s+))(?<suffix>\p{Lu}.*)~';
foreach ($names as $key => $name) {
if (preg_match($pattern, $name, $matches)) {
$names[$key] = $matches['prefix'] . mb_strtoupper($matches['suffix']);
} else {
$names[$key] = mb_strtoupper($name);
}
}
print_r($names);
он производит следующий результат для входного массива выше:
Array
(
[0] => MacARTHUR
[1] => MACARTHUR
[2] => ÜtaTEST
[3] => de WIT
)
Краткое объяснение регулярного выражения:
(?<prefix> # name of the captured group
(?: # ignore this group
\p{Lu}.+ # any uppercase character followed by any character
| # OR
.+\s+ # any character followed by white space
)
)
(?<suffix> # name of the captured group
\p{Lu}.* # any uppercase character followed by any character
)
Ответ 3
Вот базовый алгоритм, который позволяет избежать критических регулярных выражений:
- Создайте многобайтовый массив символов для литеральной фамилии (как это существует в базе данных).
- Создайте второй массив символов в многозаходной заглавной форме.
- Пересекайте оба массива, чтобы определить индекс конечного заглавного символа.
- Объединить буквенную фамилию через индекс с заглавной формой после индекса.
В кодовой форме:
<?php
$names = [
'MacArthur',
'McDavid',
'LeBlanc',
'McIntyre',
'de Wit',
'Macmaster',
'Macintosh',
'MacMac',
'die Über',
'Van der Beek',
'johnson',
'Lindström',
'Cehlárik',
];
// Uppercase after the last capital letter
function normalizeSurname($name) {
// Split surname into a Unicode character array
$chars = preg_split('//u', $name, -1, PREG_SPLIT_NO_EMPTY);
// Capitalize surname and split into a character array
$name_upper = mb_convert_case($name, MB_CASE_UPPER);
$chars_upper = preg_split('//u', $name_upper, -1, PREG_SPLIT_NO_EMPTY);
// Find the index of the last capitalize letter
@$last_capital_idx = array_slice(array_keys(array_intersect($chars, $chars_upper)), -1)[0] ?: 0;
// Concatenate the literal surname up to the index, and capitalized surname thereafter
return mb_substr($name, 0, $last_capital_idx) . mb_substr($name_upper, $last_capital_idx);
}
// Loop through the surnames and display in normalized form
foreach($names as $name) {
echo sprintf("%s -> %s\n",
$name,
normalizeSurname($name)
);
}
Вы получите вывод, например:
MacArthur -> MacARTHUR
McDavid -> McDAVID
LeBlanc -> LeBLANC
McIntyre -> McINTYRE
de Wit -> de WIT
Macmaster -> MACMASTER
Macintosh -> MACINTOSH
MacMac -> MacMAC
die Über -> die ÜBER
Van der Beek -> Van der BEEK
johnson -> JOHNSON
Lindström -> LINDSTRÖM
Cehlárik -> CEHLÁRIK
Это делает предположение о том, что заглавная буква целиком должна быть заглавной. Было бы легко изменить это поведение.
Ответ 4
$string = "McBain";
preg_match('/([A-Z][a-z]+\h*)$/', $string, $matches);
/**
Added qualifier for if no match found
**/
if(!empty($matches[1])){
// $upperString = str_replace($matches[1], strtoupper($matches[1]),$string);
// replace only last occurance of string:
$pos = strrpos($string, $matches[1]);
if($pos !== false)
{
$upperString = substr_replace($string, strtoupper($matches[1]), $pos, strlen($matches[1]));
}
}
else {
$upperString = strtoupper($string);
}
print $upperString;
Результат:
$string = "McBain ";
$upperString = "McBAIN";
$string = "Mac Hartin";
$upperString = "Mac HARTIN";
$string = "Macaroni ";
$upperString = "MACARONI";
$string = "jacaroni";
$upperString = "JACARONI";
$string = "MacMac";
$upperString = "MacMAC";
(Также добавлено \h*
в регулярное выражение, чтобы поймать любые пробелы.)
ссылка для поиска/замены последнего события.
Ответ 5
<?php
$string = "MacArthur";
$count = 0;
$finished = "";
$chars = str_split($string);
foreach($chars as $char){
if(ctype_upper($char)){
$count++;
}
if($count == 2){
$finished .= strtoupper($char);
}
else{
$finished .= $char;
}
}
echo $finished;
Ответ 6
Вот код для прописных букв всех символов после последнего верхнего регистра в строке.
preg_replace_callback('/[A-Z][^A-Z]+$/', function($match) {
return strtoupper($match[0]);
}, $str);
Попробуйте использовать тестовые примеры из вашего вопроса: https://repl.it/NYcR/5
Ответ 7
Чтобы отличаться от остальных ответов, вы можете попробовать что-то вроде этого.
$names = array(
'MacArthur',
'Macarthur',
'ÜtaTest',
'de Wit'
);
function fixSurnameA($item) {
$lname = mb_strtolower($item);
$nameArrayA = str_split($item,1);
$nameArrayB = str_split($lname,1);
$result = array_diff($nameArrayA, $nameArrayB);
$keys = array_keys($result);
$key = max($keys);
if(count($keys)>=2 or (count($keys)==1 and $key>0)) {
$pre = substr($item, 0, $key);
$suf = mb_strtoupper(substr($item, $key));
echo $pre.$suf."\n";
} else {
echo $item."\n";
}
}
function fixSurnameB($item) {
$lname = mb_strtolower($item);
$nameArrayA = str_split($item,1);
$nameArrayB = str_split($lname,1);
$result = array_diff($nameArrayA, $nameArrayB);
$keys = array_keys($result);
$key = max($keys);
$pre = substr($item, 0, $key);
$suf = mb_strtoupper(substr($item, $key));
echo $pre.$suf."\n";
}
array_walk($names,'fixSurnameA');
/* MacARTHUR
Macarthur
ÜtaTEST
de WIT
*/
array_walk($names,'fixSurnameB');
/* MacARTHUR
MACARTHUR
ÜtaTEST
de WIT
*/
Проверьте это на PHP SandBox