许多联系人管理程序都这样做 - 你输入一个名字(例如,"John W. Smith"),它会自动将其分解为:
名字:约翰
中间名: W.
姓:史密斯
同样,它也会找出像"简·史密斯夫人"和"约翰·多伊博士"这样的事情.也正确(假设您允许名称中包含"prefix"和"suffix"等字段).
我认为这是人们想要做的相当普遍的事情......所以问题是......你会怎么做?有一个简单的算法吗?也许正则表达式?
我正在使用.NET解决方案,但我并不挑剔.
更新:我感谢没有简单的解决方案可以涵盖所有边缘案例和文化...但是,为了论证,你需要说明你需要这个名字(填写表格 - 比如说,税收或其他政府表格 - 是一种情况,您必须将名称输入固定字段,无论您是否喜欢),但您不一定要强迫用户将其名称输入到离散字段中(减少输入=更容易新手用户).
你想让程序"猜测"(尽可能最好)在第一个,中间的,最后一个等等.如果可以的话,看看Microsoft Outlook如何为联系人做这个 - 它让你输入名字,但是如果你需要澄清,你可以打开一个额外的小窗口.我会做同样的事情 - 给用户提供窗口以防他们想要以离散的部分输入名称 - 但允许在一个框中输入名称并进行涵盖大多数常见名称的"最佳猜测" .
如果你必须进行这种解析,我相信你会在这里得到很多好的建议.
我的建议是 - 不要做这个解析.
而是创建输入字段,以便信息已经分离出来.有标题,名字,中间名,姓氏,后缀等单独的字段.
我知道这是旧的,可能是我已经找不到的地方的答案,但由于我找不到任何对我有用的东西,这就是我提出的,我认为它与Google Contacts和Microsoft Outlook非常相似.它不能很好地处理边缘情况,但对于一个好的CRM类型的应用程序,总是可以要求用户解决这些问题(在我的应用程序中我实际上一直有单独的字段,但我需要这个来从另一个应用程序导入数据只有一个字段):
public static void ParseName(this string s, out string prefix, out string first, out string middle, out string last, out string suffix) { prefix = ""; first = ""; middle = ""; last = ""; suffix = ""; // Split on period, commas or spaces, but don't remove from results. Listparts = Regex.Split(s, @"(?<=[., ])").ToList(); // Remove any empty parts for (int x = parts.Count - 1; x >= 0; x--) if (parts[x].Trim() == "") parts.RemoveAt(x); if (parts.Count > 0) { // Might want to add more to this list string[] prefixes = { "mr", "mrs", "ms", "dr", "miss", "sir", "madam", "mayor", "president" }; // If first part is a prefix, set prefix and remove part string normalizedPart = parts.First().Replace(".", "").Replace(",", "").Trim().ToLower(); if (prefixes.Contains(normalizedPart)) { prefix = parts[0].Trim(); parts.RemoveAt(0); } } if (parts.Count > 0) { // Might want to add more to this list, or use code/regex for roman-numeral detection string[] suffixes = { "jr", "sr", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix", "x", "xi", "xii", "xiii", "xiv", "xv" }; // If last part is a suffix, set suffix and remove part string normalizedPart = parts.Last().Replace(".", "").Replace(",", "").Trim().ToLower(); if (suffixes.Contains(normalizedPart)) { suffix = parts.Last().Replace(",", "").Trim(); parts.RemoveAt(parts.Count - 1); } } // Done, if no more parts if (parts.Count == 0) return; // If only one part left... if (parts.Count == 1) { // If no prefix, assume first name, otherwise last // i.e.- "Dr Jones", "Ms Jones" -- likely to be last if(prefix == "") first = parts.First().Replace(",", "").Trim(); else last = parts.First().Replace(",", "").Trim(); } // If first part ends with a comma, assume format: // Last, First [...First...] else if (parts.First().EndsWith(",")) { last = parts.First().Replace(",", "").Trim(); for (int x = 1; x < parts.Count; x++) first += parts[x].Replace(",", "").Trim() + " "; first = first.Trim(); } // Otherwise assume format: // First [...Middle...] Last else { first = parts.First().Replace(",", "").Trim(); last = parts.Last().Replace(",", "").Trim(); for (int x = 1; x < parts.Count - 1; x++) middle += parts[x].Replace(",", "").Trim() + " "; middle = middle.Trim(); } }
对不起,代码很长很难看,我还没有去清理它.它是一个C#扩展,所以你会像以下一样使用它:
string name = "Miss Jessica Dark-Angel Alba"; string prefix, first, middle, last, suffix; name.ParseName(out prefix, out first, out middle, out last, out suffix);
对此没有简单的解决方案.名称构造因文化而异,甚至在英语世界中也有前缀和后缀,这些前缀和后缀不一定是名称的一部分.
一个基本的方法是在字符串的开头寻找敬意(例如,"Hon.John Doe")和最后的数字或其他字符串(例如,"John Doe IV","John Doe Jr."),但是你所能做的就是应用一套启发式方法,并希望最好.
查找未处理名称列表并根据它测试算法可能很有用.不过,我不知道那里有什么预先包装好的东西.
你可能不需要做任何真正的事情.这样的事情应该有效.
Name = Name.Trim(); arrNames = Name.Split(' '); if (arrNames.Length > 0) { GivenName = arrNames[0]; } if (arrNames.Length > 1) { FamilyName = arrNames[arrNames.Length - 1]; } if (arrNames.Length > 2) { MiddleName = string.Join(" ", arrNames, 1, arrNames.Length - 2); }
您可能还想先检查标题.