我有一个名词数据库(ex“house”,“感叹号”,“苹果”),我需要在我的应用程序中输出和描述。很难用一个自然的句子来描述一个项目而不使用“a”或“an” - “房子很大”,“感叹号很小”等等。
我是否可以在PHP中使用任何函数,库或hack来确定用A或AN描述任何给定名词是否更合适?
答案 0 :(得分:8)
我需要这个用于C#项目,所以这里是上面提到的Python code的C#端口。确保在源文件中包含using System.Text.RegularExpressions;
。
private string GetIndefiniteArticle(string noun_phrase)
{
string word = null;
var m = Regex.Match(noun_phrase, @"\w+");
if (m.Success)
word = m.Groups[0].Value;
else
return "an";
var wordi = word.ToLower();
foreach (string anword in new string[] { "euler", "heir", "honest", "hono" })
if (wordi.StartsWith(anword))
return "an";
if (wordi.StartsWith("hour") && !wordi.StartsWith("houri"))
return "an";
var char_list = new char[] { 'a', 'e', 'd', 'h', 'i', 'l', 'm', 'n', 'o', 'r', 's', 'x' };
if (wordi.Length == 1)
{
if (wordi.IndexOfAny(char_list) == 0)
return "an";
else
return "a";
}
if (Regex.Match(word, "(?!FJO|[HLMNS]Y.|RY[EO]|SQU|(F[LR]?|[HL]|MN?|N|RH?|S[CHKLMNPTVW]?|X(YL)?)[AEIOU])[FHLMNRSX][A-Z]").Success)
return "an";
foreach (string regex in new string[] { "^e[uw]", "^onc?e\b", "^uni([^nmd]|mo)", "^u[bcfhjkqrst][aeiou]" })
{
if (Regex.IsMatch(wordi, regex))
return "a";
}
if (Regex.IsMatch(word, "^U[NK][AIEO]"))
return "a";
else if (word == word.ToUpper())
{
if (wordi.IndexOfAny(char_list) == 0)
return "an";
else
return "a";
}
if (wordi.IndexOfAny(new char[] { 'a', 'e', 'i', 'o', 'u' }) == 0)
return "an";
if (Regex.IsMatch(wordi, "^y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt)"))
return "an";
return "a";
}
答案 1 :(得分:7)
我也在寻找这样的解决方案,但是在JavaScript中。所以我将它移植到JS,您可以在github https://github.com/rigoneri/indefinite-article.js
中查看实际项目以下是代码段:
function indefinite_article(phrase) {
// Getting the first word
var match = /\w+/.exec(phrase);
if (match)
var word = match[0];
else
return "an";
var l_word = word.toLowerCase();
// Specific start of words that should be preceeded by 'an'
var alt_cases = ["honest", "hour", "hono"];
for (var i in alt_cases) {
if (l_word.indexOf(alt_cases[i]) == 0)
return "an";
}
// Single letter word which should be preceeded by 'an'
if (l_word.length == 1) {
if ("aedhilmnorsx".indexOf(l_word) >= 0)
return "an";
else
return "a";
}
// Capital words which should likely be preceeded by 'an'
if (word.match(/(?!FJO|[HLMNS]Y.|RY[EO]|SQU|(F[LR]?|[HL]|MN?|N|RH?|S[CHKLMNPTVW]?|X(YL)?)[AEIOU])[FHLMNRSX][A-Z]/)) {
return "an";
}
// Special cases where a word that begins with a vowel should be preceeded by 'a'
regexes = [/^e[uw]/, /^onc?e\b/, /^uni([^nmd]|mo)/, /^u[bcfhjkqrst][aeiou]/]
for (var i in regexes) {
if (l_word.match(regexes[i]))
return "a"
}
// Special capital words (UK, UN)
if (word.match(/^U[NK][AIEO]/)) {
return "a";
}
else if (word == word.toUpperCase()) {
if ("aedhilmnorsx".indexOf(l_word[0]) >= 0)
return "an";
else
return "a";
}
// Basic method of words that begin with a vowel being preceeded by 'an'
if ("aeiou".indexOf(l_word[0]) >= 0)
return "an";
// Instances where y follwed by specific letters is preceeded by 'an'
if (l_word.match(/^y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt)/))
return "an";
return "a";
}
答案 2 :(得分:6)
您想要的是确定适当的不定冠词。 Lingua::EN::Inflect
是一个很棒的Perl模块。我已经提取了相关代码并将其粘贴在下面。它只是一堆案例和一些正则表达式,因此移植到PHP应该不难。如果有人有兴趣,朋友将其移植到Python here。
# 2. INDEFINITE ARTICLES
# THIS PATTERN MATCHES STRINGS OF CAPITALS STARTING WITH A "VOWEL-SOUND"
# CONSONANT FOLLOWED BY ANOTHER CONSONANT, AND WHICH ARE NOT LIKELY
# TO BE REAL WORDS (OH, ALL RIGHT THEN, IT'S JUST MAGIC!)
my $A_abbrev = q{
(?! FJO | [HLMNS]Y. | RY[EO] | SQU
| ( F[LR]? | [HL] | MN? | N | RH? | S[CHKLMNPTVW]? | X(YL)?) [AEIOU])
[FHLMNRSX][A-Z]
};
# THIS PATTERN CODES THE BEGINNINGS OF ALL ENGLISH WORDS BEGINING WITH A
# 'y' FOLLOWED BY A CONSONANT. ANY OTHER Y-CONSONANT PREFIX THEREFORE
# IMPLIES AN ABBREVIATION.
my $A_y_cons = 'y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt)';
# EXCEPTIONS TO EXCEPTIONS
my $A_explicit_an = enclose join '|',
(
"euler",
"hour(?!i)", "heir", "honest", "hono",
);
my $A_ordinal_an = enclose join '|',
(
"[aefhilmnorsx]-?th",
);
my $A_ordinal_a = enclose join '|',
(
"[bcdgjkpqtuvwyz]-?th",
);
sub A {
my ($str, $count) = @_;
my ($pre, $word, $post) = ( $str =~ m/\A(\s*)(?:an?\s+)?(.+?)(\s*)\Z/i );
return $str unless $word;
my $result = _indef_article($word,$count);
return $pre.$result.$post;
}
sub AN { goto &A }
sub _indef_article {
my ( $word, $count ) = @_;
$count = $persistent_count
if !defined($count) && defined($persistent_count);
return "$count $word"
if defined $count && $count!~/^($PL_count_one)$/io;
# HANDLE USER-DEFINED VARIANTS
my $value;
return "$value $word"
if defined($value = ud_match($word, @A_a_user_defined));
# HANDLE ORDINAL FORMS
$word =~ /^($A_ordinal_a)/i and return "a $word";
$word =~ /^($A_ordinal_an)/i and return "an $word";
# HANDLE SPECIAL CASES
$word =~ /^($A_explicit_an)/i and return "an $word";
$word =~ /^[aefhilmnorsx]$/i and return "an $word";
$word =~ /^[bcdgjkpqtuvwyz]$/i and return "a $word";
# HANDLE ABBREVIATIONS
$word =~ /^($A_abbrev)/ox and return "an $word";
$word =~ /^[aefhilmnorsx][.-]/i and return "an $word";
$word =~ /^[a-z][.-]/i and return "a $word";
# HANDLE CONSONANTS
$word =~ /^[^aeiouy]/i and return "a $word";
# HANDLE SPECIAL VOWEL-FORMS
$word =~ /^e[uw]/i and return "a $word";
$word =~ /^onc?e\b/i and return "a $word";
$word =~ /^uni([^nmd]|mo)/i and return "a $word";
$word =~ /^ut[th]/i and return "an $word";
$word =~ /^u[bcfhjkqrst][aeiou]/i and return "a $word";
# HANDLE SPECIAL CAPITALS
$word =~ /^U[NK][AIEO]?/ and return "a $word";
# HANDLE VOWELS
$word =~ /^[aeiou]/i and return "an $word";
# HANDLE y... (BEFORE CERTAIN CONSONANTS IMPLIES (UNNATURALIZED) "i.." SOUND)
$word =~ /^($A_y_cons)/io and return "an $word";
# OTHERWISE, GUESS "a"
return "a $word";
}
答案 3 :(得分:0)
制作一个带元音的数组。检查您正在检查的单词的第一个字母是否在元音数组中。除了处理首字母缩略词时,它会起作用。
答案 4 :(得分:0)
从头开始编写应该很容易,tbh。如果一个单词以元音开头,则会得到'a';如果它以辅音开头,则会得到'an'。以编程方式,这很容易 - 如果你有任何边缘情况(例如你可能使用BBC英式风格'一个历史性的场合')你可以单独处理它们。
有点像使用变形器,只有'a'/'an'语法规则而不是复数。看看CakePHP或Rails如何处理变形,以便更全面地讨论这个概念,包括如何处理边缘情况 - 例如,你不想将'鹿'变成复数形式的'鹿'或'鹅' 'gooses',所以他们需要单独处理,就像你自己的边缘情况,如'宇宙'或吸气/非吸气'H'。
答案 5 :(得分:0)
正在寻找这样的解决方案,谢谢marcog。这是尝试移植你朋友的python版本(我不知道python或perl,所以可能有一些错误):
function indefinite_article($word) {
// Lowercase version of the word
$word_lower = strtolower($word);
// An 'an' word (specific start of words that should be preceeded by 'an')
$an_words = array('euler', 'heir', 'honest', 'hono');
foreach($an_words as $an_word) {
if(substr($word_lower,0,strlen($an_word)) == $an_word) return "an";
}
if(substr($word_lower,0,4) == "hour" and substr($word_lower,0,5) != "houri") return "an";
// An 'an' letter (single letter word which should be preceeded by 'an')
$an_letters = array('a','e','f','h','i','l','m','n','o','r','s','x');
if(strlen($word) == 1) {
if(in_array($word_lower,$an_letters)) return "an";
else return "a";
}
// Capital words which should likely by preceeded by 'an'
if(preg_match('/(?!FJO|[HLMNS]Y.|RY[EO]|SQU|(F[LR]?|[HL]|MN?|N|RH?|S[CHKLMNPTVW]?|X(YL)?)[AEIOU])[FHLMNRSX][A-Z]/', $word)) return "an";
// Special cases where a word that begins with a vowel should be preceeded by 'a'
$regex_array = array('^e[uw]','^onc?e\b','^uni([^nmd]|mo)','^u[bcfhjkqrst][aeiou]');
foreach($regex_array as $regex) {
if(preg_match('/'.$regex.'/',$word_lower)) return "a";
}
// Special capital words
if(preg_match('/^U[NK][AIEO]/',$word)) return "a";
// Not sure what this does
else if($word == strtoupper($word)) {
$array = array('a','e','d','h','i','l','m','n','o','r','s','x');
if(in_array($word_lower[0],$array)) return "an";
else return "a";
}
// Basic method of words that begin with a vowel being preceeded by 'an'
$vowels = array('a','e','i','o','u');
if(in_array($word_lower[0],$vowels)) return "an";
// Instances where y follwed by specific letters is preceeded by 'an'
if(preg_match('/^y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt)/', $word_lower)) return "an";
// Default to 'a'
return "a";
}
有一点(在评论下方“//不确定这是做什么”),我不确定它做了什么。如果有人能搞清楚,我会很高兴知道。
答案 6 :(得分:0)
基于规则的系统的问题在于它们处理边缘情况很差,并且它们很复杂。如果您可以根据实际数据做出决策,那么您会做得更好。在this answer中,我描述了如何使用维基百科构建查找字典,并使用这样的字典链接到(非常简单的)javascript实现。
前缀字典可以很好地处理首字母缩略词和数字,但是通过一些努力你可能会做得更好。
答案 7 :(得分:0)
我已经编写了一个流行的JS a-vs-an code的PHP端口,如此stackoverflow帖子https://stackoverflow.com/a/1288473/1526020中所述。
Github页面:https://github.com/UseAllFive/a-vs-an。
E.g。
$result = $aVsAn->query('0800 number');
print_r($result);
返回
Array
(
[aCount] => 8
[anCount] => 25
[prefix] => 08
[article] => an
)