typedef typename Tail::inUnion dummy;
Однако я не уверен, что реализация inUnion верна. Если я правильно понимаю, этот класс не должен быть создан, поэтому вкладка «fail» никогда не будет автоматически терпеть неудачу. Возможно, было бы лучше указать, находится ли тип в объединении или нет с простым булевым значением.
template
struct Contains; template struct Contains > { enum { result = Contains ::result }; }; template struct Contains > { enum { result = true }; }; template struct Contains { enum { result = false }; }; PS: Посмотрите на Boost :: Variant
PS2: посмотрите на typelists , особенно в книге Андрея Александреску: Modern C ++ Design
Вот автономный пример с данными тестирования, которыми легко управляют.
С этим примером, если у Вас есть имя больше чем с тремя частями, тогда весь "дополнительный" материал будет вставлен в поле LAST_NAME. Исключение сделано для определенных строк, которые идентифицируются как "заголовки", такие как "DR", "MRS" и "MR".
, Если второе имя отсутствует, то Вы просто получаете FIRST_NAME и LAST_NAME (MIDDLE_NAME будет ПУСТЫМ).
Вы могли разбиться, это в гиганта вложило блоб ПОДСТРОК, но удобочитаемость достаточно трудна, как это - когда Вы делаете это в SQL.
Редактирование - Дескриптор следующие особые случаи:
1 - Поле имени ПУСТОЕ
2 - Поле имени содержит продвижение / конечные пробелы
3 - Поле имени имеет> 1 последовательное пространство в имени
4 - Поле имени содержит ТОЛЬКО имя
5 - Включают исходное полное имя в окончательный результат как отдельный столбец, для удобочитаемости
6 - Дескриптор определенный список префиксов как отдельный столбец
SELECT
FIRST_NAME.ORIGINAL_INPUT_DATA
,FIRST_NAME.TITLE
,FIRST_NAME.FIRST_NAME
,CASE WHEN 0 = CHARINDEX(' ',FIRST_NAME.REST_OF_NAME)
THEN NULL --no more spaces? assume rest is the last name
ELSE SUBSTRING(
FIRST_NAME.REST_OF_NAME
,1
,CHARINDEX(' ',FIRST_NAME.REST_OF_NAME)-1
)
END AS MIDDLE_NAME
,SUBSTRING(
FIRST_NAME.REST_OF_NAME
,1 + CHARINDEX(' ',FIRST_NAME.REST_OF_NAME)
,LEN(FIRST_NAME.REST_OF_NAME)
) AS LAST_NAME
FROM
(
SELECT
TITLE.TITLE
,CASE WHEN 0 = CHARINDEX(' ',TITLE.REST_OF_NAME)
THEN TITLE.REST_OF_NAME --No space? return the whole thing
ELSE SUBSTRING(
TITLE.REST_OF_NAME
,1
,CHARINDEX(' ',TITLE.REST_OF_NAME)-1
)
END AS FIRST_NAME
,CASE WHEN 0 = CHARINDEX(' ',TITLE.REST_OF_NAME)
THEN NULL --no spaces @ all? then 1st name is all we have
ELSE SUBSTRING(
TITLE.REST_OF_NAME
,CHARINDEX(' ',TITLE.REST_OF_NAME)+1
,LEN(TITLE.REST_OF_NAME)
)
END AS REST_OF_NAME
,TITLE.ORIGINAL_INPUT_DATA
FROM
(
SELECT
--if the first three characters are in this list,
--then pull it as a "title". otherwise return NULL for title.
CASE WHEN SUBSTRING(TEST_DATA.FULL_NAME,1,3) IN ('MR ','MS ','DR ','MRS')
THEN LTRIM(RTRIM(SUBSTRING(TEST_DATA.FULL_NAME,1,3)))
ELSE NULL
END AS TITLE
--if you change the list, don't forget to change it here, too.
--so much for the DRY prinicple...
,CASE WHEN SUBSTRING(TEST_DATA.FULL_NAME,1,3) IN ('MR ','MS ','DR ','MRS')
THEN LTRIM(RTRIM(SUBSTRING(TEST_DATA.FULL_NAME,4,LEN(TEST_DATA.FULL_NAME))))
ELSE LTRIM(RTRIM(TEST_DATA.FULL_NAME))
END AS REST_OF_NAME
,TEST_DATA.ORIGINAL_INPUT_DATA
FROM
(
SELECT
--trim leading & trailing spaces before trying to process
--disallow extra spaces *within* the name
REPLACE(REPLACE(LTRIM(RTRIM(FULL_NAME)),' ',' '),' ',' ') AS FULL_NAME
,FULL_NAME AS ORIGINAL_INPUT_DATA
FROM
(
--if you use this, then replace the following
--block with your actual table
SELECT 'GEORGE W BUSH' AS FULL_NAME
UNION SELECT 'SUSAN B ANTHONY' AS FULL_NAME
UNION SELECT 'ALEXANDER HAMILTON' AS FULL_NAME
UNION SELECT 'OSAMA BIN LADEN JR' AS FULL_NAME
UNION SELECT 'MARTIN J VAN BUREN SENIOR III' AS FULL_NAME
UNION SELECT 'TOMMY' AS FULL_NAME
UNION SELECT 'BILLY' AS FULL_NAME
UNION SELECT NULL AS FULL_NAME
UNION SELECT ' ' AS FULL_NAME
UNION SELECT ' JOHN JACOB SMITH' AS FULL_NAME
UNION SELECT ' DR SANJAY GUPTA' AS FULL_NAME
UNION SELECT 'DR JOHN S HOPKINS' AS FULL_NAME
UNION SELECT ' MRS SUSAN ADAMS' AS FULL_NAME
UNION SELECT ' MS AUGUSTA ADA KING ' AS FULL_NAME
) RAW_DATA
) TEST_DATA
) TITLE
) FIRST_NAME
"заголовка" Я не уверен в SQL-сервере, но в пост-ГРЭС Вы могли сделать что-то вроде этого:
SELECT
SUBSTRING(fullname, '(\\w+)') as firstname,
SUBSTRING(fullname, '\\w+\\s(\\w+)\\s\\w+') as middle,
COALESCE(SUBSTRING(fullname, '\\w+\\s\\w+\\s(\\w+)'), SUBSTRING(fullname, '\\w+\\s(\\w+)')) as lastname
FROM
public.person
regex выражения могли, вероятно, быть немного более краткими; но Вы понимаете. Это между прочим не работает на людей, имеющих два двойных имени (в Нидерландах, у нас есть этот много 'Jan van der Ploeg'), таким образом, я был бы очень осторожен с результатами.
Как все остальные говорят, Вы не можете от простого программируемого пути.
Рассматривают эти примеры:
президент "George Herbert Walker Bush" (Первая середина середины В последний раз)
убийца Президента "John Wilkes Booth" (Первая середина В последний раз)
Гитарист "Eddie Van Halen" (Сначала Последний Последний)
И его мама, вероятно, называет его Edward Lodewijk Van Halen (Первая середина В последний раз Длятся)
Знаменитый потерпевший кораблекрушение "Mary Ann Summers" (Сначала Первый Последний)
председатель Республиканской партии Нью-Мексико "Fernando C de Baca" (Сначала Последний Последний Последний)
Я когда-то сделал 500 символьных регулярных выражений, чтобы проанализировать сначала, продлиться и вторые имена от произвольной строки. Даже с тем гудением regex, это только обошло 97%-ю точность из-за полного несоответствия входа. Однако, лучше чем ничего.
Вот хранимая процедура, которая поместит первое слово, найденное в Имя, последнее слово в Фамилию и все промежуточное во Второе имя.
create procedure [dbo].[import_ParseName]
(
@FullName nvarchar(max),
@FirstName nvarchar(255) output,
@MiddleName nvarchar(255) output,
@LastName nvarchar(255) output
)
as
begin
set @FirstName = ''
set @MiddleName = ''
set @LastName = ''
set @FullName = ltrim(rtrim(@FullName))
declare @ReverseFullName nvarchar(max)
set @ReverseFullName = reverse(@FullName)
declare @lengthOfFullName int
declare @endOfFirstName int
declare @beginningOfLastName int
set @lengthOfFullName = len(@FullName)
set @endOfFirstName = charindex(' ', @FullName)
set @beginningOfLastName = @lengthOfFullName - charindex(' ', @ReverseFullName) + 1
set @FirstName = case when @endOfFirstName <> 0
then substring(@FullName, 1, @endOfFirstName - 1)
else ''
end
set @MiddleName = case when (@endOfFirstName <> 0 and @beginningOfLastName <> 0 and @beginningOfLastName > @endOfFirstName)
then ltrim(rtrim(substring(@FullName, @endOfFirstName , @beginningOfLastName - @endOfFirstName)))
else ''
end
set @LastName = case when @beginningOfLastName <> 0
then substring(@FullName, @beginningOfLastName + 1 , @lengthOfFullName - @beginningOfLastName)
else ''
end
return
end
И вот я называющий его.
DECLARE @FirstName nvarchar(255),
@MiddleName nvarchar(255),
@LastName nvarchar(255)
EXEC [dbo].[import_ParseName]
@FullName = N'Scott The Other Scott Kowalczyk',
@FirstName = @FirstName OUTPUT,
@MiddleName = @MiddleName OUTPUT,
@LastName = @LastName OUTPUT
print @FirstName
print @MiddleName
print @LastName
output:
Scott
The Other Scott
Kowalczyk
Согласно протестам, которые были уже повышены относительно пробелов на имена и другие аномалии, следующий код, по крайней мере, обработает 98% имен. (Отметьте: грязный SQL, потому что у меня нет regex опции в базе данных, которую я использую.)
** Предупреждение: грязный SQL следует:
create table parsname (fullname char(50), name1 char(30), name2 char(30), name3 char(30), name4 char(40));
insert into parsname (fullname) select fullname from ImportTable;
update parsname set name1 = substring(fullname, 1, locate(' ', fullname)),
fullname = ltrim(substring(fullname, locate(' ', fullname), length(fullname)))
where locate(' ', rtrim(fullname)) > 0;
update parsname set name2 = substring(fullname, 1, locate(' ', fullname)),
fullname = ltrim(substring(fullname, locate(' ', fullname), length(fullname)))
where locate(' ', rtrim(fullname)) > 0;
update parsname set name3 = substring(fullname, 1, locate(' ', fullname)),
fullname = ltrim(substring(fullname, locate(' ', fullname), length(fullname)))
where locate(' ', rtrim(fullname)) > 0;
update parsname set name4 = substring(fullname, 1, locate(' ', fullname)),
fullname = ltrim(substring(fullname, locate(' ', fullname), length(fullname)))
where locate(' ', rtrim(fullname)) > 0;
// fullname now contains the last word in the string.
select fullname as FirstName, '' as MiddleName, '' as LastName from parsname where fullname is not null and name1 is null and name2 is null
union all
select name1 as FirstName, name2 as MiddleName, fullname as LastName from parsname where name1 is not null and name3 is null
код работает путем составления временной таблицы (parsname) и маркирования fullname пробелами. Любые имена, заканчивающиеся со значениями в name3 или name4, являются несоответствующими и должны будут иметься дело с по-другому.
Я сделал бы это как итеративный процесс.
1) Дамп таблица к плоскому файлу для работы с.
2) Запись простая программа для разбивания Имен, использующих пространство как разделитель, где маркер первых является именем если существует 3 маркера тогда, маркер 2 является вторым именем, и маркер 3 является фамилией. Если существует 2 маркера тогда, второй маркер является фамилией. (Perl, Java или C/C++, язык не имеет значения)
3) Глазное яблоко результаты. Ищите имена, которые не соответствуют этому правилу.
4) Используя тот пример, создайте новое правило обработать то исключение...
5) Промывка и Повторение
В конечном счете Вы получите программу, которая фиксирует все Ваши данные.
Как № 1 сказал, это не тривиально. Написанные через дефис фамилии, инициалы, двойные имена, обратная последовательность имени и множество других аномалий могут разрушить Вашу тщательно обработанную функцию.
Вы могли пользоваться сторонней библиотекой (разъем/правовая оговорка - я работал над этим продуктом):
Вы уверены, что Полное Официальное имя будет всегда включать Сначала, середина и В последний раз? Я знаю людей, которые имеют только одно имя как Полное Официальное имя, и честно я не уверен, является ли это их Именем или Фамилией.:-) я также знаю людей, которые имеют больше чем одно название Fisrt в их официальном имени, но не имеют Второго имени. И существуют некоторые люди, которые имеют несколько Вторых имен.
Тогда существует также порядок имен в Полном Официальном имени. Насколько я знаю в некоторых азиатских культурах, Фамилия на первом месте в Полном Официальном имени.
На более практической ноте, Вы могли разделить Полное имя на пробеле и угрозе первый маркер как Имя и последний маркер (или единственный маркер в случае только одного имени) как Фамилия. Хотя это предполагает, что порядок всегда будет тем же.
Если Вы не имеете очень, данные очень хорошего поведения, это - нетривиальная проблема. Наивный подход должен был бы маркировать на пробеле и предположить, что результат с тремя маркерами [сначала, середина, в последний раз] и результат с двумя маркерами [сначала, в последний раз], но Вы оказываетесь перед необходимостью иметь дело с фамилиями многословными (например, "Ван-Бюрен") и несколько вторых имен.
Инвертируйте проблему, добавьте столбцы, чтобы содержать отдельные части и объединить их для получения полного имени.
причина это будет лучшее , ответ - то, что нет никакого гарантируемого способа выяснить, что человек зарегистрировался как их имя, и каково их второе имя.
, Например, как Вы разделили бы это?
Jan Olav Olsen Heggelien
Это, будучи fictious, является официальным именем в Норвегии, и, но не имело бы к, мог быть разделен как это:
First name: Jan Olav
Middle name: Olsen
Last name: Heggelien
или, как это:
First name: Jan Olav
Last name: Olsen Heggelien
или, как это:
First name: Jan
Middle name: Olav
Last name: Olsen Heggelien
я предположил бы, что подобные происшествия могут быть найдены на большинстве языков.
Так вместо того, чтобы пробовать к интерпретации данных, которые не имеют достаточной информации для разбираний в нем, сохраните корректную интерпретацию и объединение для получения полного имени.
Трудно ответить, не зная, как "полное имя" отформатировано.
Это могла быть "Фамилия, Второе имя Имени" или "Фамилия Второго имени Имени", и т.д.
В основном необходимо будет использовать ПОДСТРОКА функция
SUBSTRING ( expression , start , length )
И вероятно функция CHARINDEX
CHARINDEX (substr, expression)
Для выяснения запуска и длины для каждой части, которую Вы хотите извлечь.
, Таким образом, скажем, формат является "Фамилией Имени", Вы могли (непротестированный.. но должно быть близким):
SELECT
SUBSTRING(fullname, 1, CHARINDEX(' ', fullname) - 1) AS FirstName,
SUBSTRING(fullname, CHARINDEX(' ', fullname) + 1, len(fullname)) AS LastName
FROM YourTable
я рекомендую Экспресс для learnin/building/testing регулярных выражений. Старая бесплатная версия , новая коммерческая версия
Мы, конечно, все понимаем, что нет идеального способа решить эту проблему, но некоторые решения могут помочь вам продвинуться дальше, чем другие.
В частности, довольно легко выйти за рамки простых разделителей пробелов, если у вас есть просто несколько списков общих префиксов (Mr, Dr, Mrs и т. Д.), Инфиксов (von, de, del и т. Д.), Суффиксов ( Jr, III, Sr и т. Д.) И так далее. Также полезно, если у вас есть несколько списков общих имен (на разных языках / культурах, если ваши имена разные), чтобы вы могли угадать, может ли слово в середине быть частью фамилии или нет.
BibTeX также реализует некоторые эвристики, которые помогут вам в этом; они инкапсулированы в Perl-модуле Text :: BibTeX :: Name
. Вот небольшой пример кода, который выполняет разумную работу.
use Text::BibTeX;
use Text::BibTeX::Name;
$name = "Dr. Mario Luis de Luigi Jr.";
$name =~ s/^\s*([dm]rs?.?|miss)\s+//i;
$dr=$1;
$n=Text::BibTeX::Name->new($name);
print join("\t", $dr, map "@{[ $n->part($_) ]}", qw(first von last jr)), "\n";
Если вы пытаетесь разобрать человеческое имя в PHP, я рекомендую сценарий Кейта Бекмана nameparse.php .
Скопируйте на случай, если сайт выйдет из строя:
<?
/*
Name: nameparse.php
Version: 0.2a
Date: 030507
First: 030407
License: GNU General Public License v2
Bugs: If one of the words in the middle name is Ben (or St., for that matter),
or any other possible last-name prefix, the name MUST be entered in
last-name-first format. If the last-name parsing routines get ahold
of any prefix, they tie up the rest of the name up to the suffix. i.e.:
William Ben Carey would yield 'Ben Carey' as the last name, while,
Carey, William Ben would yield 'Carey' as last and 'Ben' as middle.
This is a problem inherent in the prefix-parsing routines algorithm,
and probably will not be fixed. It's not my fault that there's some
odd overlap between various languages. Just don't name your kids
'Something Ben Something', and you should be alright.
*/
function norm_str($string) {
return trim(strtolower(
str_replace('.','',$string)));
}
function in_array_norm($needle,$haystack) {
return in_array(norm_str($needle),$haystack);
}
function parse_name($fullname) {
$titles = array('dr','miss','mr','mrs','ms','judge');
$prefices = array('ben','bin','da','dal','de','del','der','de','e',
'la','le','san','st','ste','van','vel','von');
$suffices = array('esq','esquire','jr','sr','2','ii','iii','iv');
$pieces = explode(',',preg_replace('/\s+/',' ',trim($fullname)));
$n_pieces = count($pieces);
switch($n_pieces) {
case 1: // array(title first middles last suffix)
$subp = explode(' ',trim($pieces[0]));
$n_subp = count($subp);
for($i = 0; $i < $n_subp; $i++) {
$curr = trim($subp[$i]);
$next = trim($subp[$i+1]);
if($i == 0 && in_array_norm($curr,$titles)) {
$out['title'] = $curr;
continue;
}
if(!$out['first']) {
$out['first'] = $curr;
continue;
}
if($i == $n_subp-2 && $next && in_array_norm($next,$suffices)) {
if($out['last']) {
$out['last'] .= " $curr";
}
else {
$out['last'] = $curr;
}
$out['suffix'] = $next;
break;
}
if($i == $n_subp-1) {
if($out['last']) {
$out['last'] .= " $curr";
}
else {
$out['last'] = $curr;
}
continue;
}
if(in_array_norm($curr,$prefices)) {
if($out['last']) {
$out['last'] .= " $curr";
}
else {
$out['last'] = $curr;
}
continue;
}
if($next == 'y' || $next == 'Y') {
if($out['last']) {
$out['last'] .= " $curr";
}
else {
$out['last'] = $curr;
}
continue;
}
if($out['last']) {
$out['last'] .= " $curr";
continue;
}
if($out['middle']) {
$out['middle'] .= " $curr";
}
else {
$out['middle'] = $curr;
}
}
break;
case 2:
switch(in_array_norm($pieces[1],$suffices)) {
case TRUE: // array(title first middles last,suffix)
$subp = explode(' ',trim($pieces[0]));
$n_subp = count($subp);
for($i = 0; $i < $n_subp; $i++) {
$curr = trim($subp[$i]);
$next = trim($subp[$i+1]);
if($i == 0 && in_array_norm($curr,$titles)) {
$out['title'] = $curr;
continue;
}
if(!$out['first']) {
$out['first'] = $curr;
continue;
}
if($i == $n_subp-1) {
if($out['last']) {
$out['last'] .= " $curr";
}
else {
$out['last'] = $curr;
}
continue;
}
if(in_array_norm($curr,$prefices)) {
if($out['last']) {
$out['last'] .= " $curr";
}
else {
$out['last'] = $curr;
}
continue;
}
if($next == 'y' || $next == 'Y') {
if($out['last']) {
$out['last'] .= " $curr";
}
else {
$out['last'] = $curr;
}
continue;
}
if($out['last']) {
$out['last'] .= " $curr";
continue;
}
if($out['middle']) {
$out['middle'] .= " $curr";
}
else {
$out['middle'] = $curr;
}
}
$out['suffix'] = trim($pieces[1]);
break;
case FALSE: // array(last,title first middles suffix)
$subp = explode(' ',trim($pieces[1]));
$n_subp = count($subp);
for($i = 0; $i < $n_subp; $i++) {
$curr = trim($subp[$i]);
$next = trim($subp[$i+1]);
if($i == 0 && in_array_norm($curr,$titles)) {
$out['title'] = $curr;
continue;
}
if(!$out['first']) {
$out['first'] = $curr;
continue;
}
if($i == $n_subp-2 && $next &&
in_array_norm($next,$suffices)) {
if($out['middle']) {
$out['middle'] .= " $curr";
}
else {
$out['middle'] = $curr;
}
$out['suffix'] = $next;
break;
}
if($i == $n_subp-1 && in_array_norm($curr,$suffices)) {
$out['suffix'] = $curr;
continue;
}
if($out['middle']) {
$out['middle'] .= " $curr";
}
else {
$out['middle'] = $curr;
}
}
$out['last'] = $pieces[0];
break;
}
unset($pieces);
break;
case 3: // array(last,title first middles,suffix)
$subp = explode(' ',trim($pieces[1]));
$n_subp = count($subp);
for($i = 0; $i < $n_subp; $i++) {
$curr = trim($subp[$i]);
$next = trim($subp[$i+1]);
if($i == 0 && in_array_norm($curr,$titles)) {
$out['title'] = $curr;
continue;
}
if(!$out['first']) {
$out['first'] = $curr;
continue;
}
if($out['middle']) {
$out['middle'] .= " $curr";
}
else {
$out['middle'] = $curr;
}
}
$out['last'] = trim($pieces[0]);
$out['suffix'] = trim($pieces[2]);
break;
default: // unparseable
unset($pieces);
break;
}
return $out;
}
?>