将列表转换为字符串,元音和'和'在末尾

时间:2018-05-14 22:41:59

标签: c#

我试图从列表中创建一个字符串。但是,我希望它以一种有意义的方式排列,所以不是字符串是#34;我有苹果,香蕉,爸爸的柑橘。",我希望它是" 34;我有一个苹果,一个香蕉和爸爸的柑橘。"。 (Strings是" apple"," banana"和" Papa'柑橘"。)

我宁愿不改变构成我名单的字符串;我正在处理的是一个mod,它会根据启用的其他mod修改列表,因此添加'和'每组的最后一个字符串都不能很好地工作。

总而言之,我想要的代码会将列表转换为字符串,添加' a'在以辅音开头的单词面前,' an'在带有元音的单词面前,也没有在带有撇号的单词前面。

谢谢!

3 个答案:

答案 0 :(得分:1)

目前尚不清楚您的输入是什么样的,但它可能类似于此控制台应用程序。但是,英语不是我的第一语言,所以我可能会非常错误:

static void Main(string[] args)
{
    List<string> strings = new List<string>
    {
        "apple",
        "banana",
        "Papa's citrus"
    };

    var lastNdx = strings.Count - 1;
    var sentence = "I have " + String.Join(", ", 
        strings.Select((s, ndx) =>
        {
            var ls = s.ToLower();
            string ret = "";
            if ("aeiou".Contains(ls[0]))
                ret = "an " + s;
            else if (ls.Contains("\'"))
                ret = s;
            else ret = "a " + s;
            if (ndx == lastNdx)
                ret = "and " + ret;
            return ret;
        }).ToArray() );

    Console.WriteLine(sentence);
}

答案 1 :(得分:1)

使用一些方便的扩展方法:

public static class IEnumerableExt {
    public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source) => new HashSet<T>(source);
    public static IEnumerable<T> Leave<T>(this ICollection<T> src, int drop) => src.Take(src.Count - drop);
    public static IEnumerable<T> Drop<T>(this ICollection<T> src, int drop) => (drop < 0) ? src.Leave(-drop) : src.Skip(drop);
}

public static class StringExt {
    public static string UpTo(this string s, Regex stopRE) {
        var m = stopRE.Match(s);
        if (m.Success)
            return s.Substring(0, m.Index);
        else
            return s;
    }

    public static string Join(this IEnumerable<string> strings, string sep) => String.Join(sep, strings.ToArray());
    public static bool EndsWithOneOf(this string s, params string[] endings) => endings.Any(e => s.EndsWith(e));
}

然后,您可以创建一些自定义扩展方法来解决问题:

public static class FruitExt {
    public static readonly HashSet<char> consonants = "bcdfghjklmnpqrstvwxyz".ToHashSet();
    public static bool BeginsWithConsonant(this string w) => consonants.Contains(w[0]);

    public static bool IsPossessive(this string w) => w.UpTo(new Regex(@"\s")).EndsWithOneOf("'s", "'");

    public static string WithIndefiniteArticle(this string w) => (w.BeginsWithConsonant() ? "a " : "an ") + w;
    public static string ArticleOrPossessive(this string w) => w.IsPossessive() ? w : w.WithIndefiniteArticle();
}

请注意BeginsWithConsonant对于以Y开头作为元音的那些(四个?)单词失败 - 如果它困扰你,你可以为方法添加例外。

现在答案很简单LINQ:

var ans = ("I have "+src.Drop(-1).Select(w => w.ArticleOrPossessive()).Join(", ")+" and "+src.Last().ArticleOrPossessive()+".");
在一般情况下,

DropLast并不是特别有效,但由于我们知道我们使用的是List,因此它们很好(并且数据将会无论如何都要短暂。

答案 2 :(得分:1)

这个答案适用于@Stilgar。这有点复杂,所以我做了第二个答案。

下载最新版本的CMU Pronunciation Dictionary files并将其存储在一个文件夹中。在CMUDictExt类中设置路径以使用该文件夹:

public static class CMUDictExt {
    const string cmuFolder = @"D:\";
    static IEnumerable<string> CMUFiles = Directory.EnumerateFiles(cmuFolder, "cmudict-*");
    static Regex cmudictName = new Regex(@"cmudict-(?:\d+(?:\.\d+)?[a-z]?)+\.?(.*)$", RegexOptions.Compiled);
    static string CMUFile(string ext) => CMUFiles.First(f => cmudictName.Match(f).Groups[1].Value == ext);

    static Dictionary<string, string> phones;
    static Dictionary<string, string[]> pronunciations;
    public static ILookup<string, string> SymbolWords;
    static HashSet<string> exceptions;

    static CMUDictExt() {
        phones = File.ReadLines(CMUFile("phones"))
                     .Select(l => l.Split('\t'))
                     .ToDictionary(pa => pa[0], pa => pa[1]);

        pronunciations = File.ReadLines(CMUFile(""))
                             .Where(l => !l.StartsWith(";;;"))
                             .Where(l => Char.IsLetter(l[0]))
                             .Select(l => l.Split("  ").ToArray())
                             .ToDictionary(wg => wg[0].ToLowerInvariant(), wg => wg[1].Split(' '));

        SymbolWords = pronunciations.SelectMany(wp => wp.Value.Select(s => (Word: wp.Key, s)))
                                    .ToLookup(wp => wp.s, wp => wp.Word);
        exceptions = pronunciations.Where(wp => (wp.Key.StartsWithVowel() ^ wp.Value[0].Phone() == "vowel")).Select(wp => wp.Key).ToHashSet();
    }

    public static string Phone(this string aSymbol) => phones.GetValueOrDefault(aSymbol.UpTo(ch => Char.IsDigit(ch)), String.Empty);

    static string[] emptyStringArray = new string[] {};
    public static string[] Pronunciation(this string aWord) => pronunciations.GetValueOrDefault(aWord.ToLowerInvariant(), emptyStringArray);
    public static bool HasPronunciation(this string aWord) => pronunciations.GetValueOrDefault(aWord.ToLowerInvariant(), null) != null;

    static readonly HashSet<char> vowels = "aeiou".ToHashSet<char>();
    public static bool StartsWithVowel(this string w) => vowels.Contains(w[0]);
    public static bool BeginsWithVowelSound(this string aWord) => exceptions.Contains(aWord) ? !aWord.StartsWithVowel() : aWord.StartsWithVowel(); // guess if not found
}

使用与以前类似的扩展方法:

public static class IEnumerableExt {
    public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source) => new HashSet<T>(source);
    public static IEnumerable<T> Leave<T>(this ICollection<T> src, int drop) => src.Take(src.Count - drop);
    public static IEnumerable<T> Drop<T>(this ICollection<T> src, int drop) => (drop < 0) ? src.Leave(-drop) : src.Skip(drop);
    public static T MinBy<T, TKey>(this IEnumerable<T> src, Func<T, TKey> keySelector, Comparer<TKey> keyComparer) => src.Aggregate((a, b) => keyComparer.Compare(keySelector(a), keySelector(b)) < 0 ? a : b);
    public static T MinBy<T, TKey>(this IEnumerable<T> src, Func<T, TKey> keySelector) => src.Aggregate((a, b) => Comparer<TKey>.Default.Compare(keySelector(a), keySelector(b)) < 0 ? a : b);
}

public static class StringExt {
    public static string UpTo(this string s, Regex stopRE) {
        var m = stopRE.Match(s);
        if (m.Success)
            return s.Substring(0, m.Index);
        else
            return s;
    }
    public static string UpTo(this string s, Func<char, bool> testfn) {
        var m = s.Select((ch, Index) => new { ch, Index, Success = testfn(ch) }).FirstOrDefault(cit => cit.Success);
        if (m != null && m.Success)
            return s.Substring(0, m.Index);
        else
            return s;
    }

    public static string Join(this IEnumerable<string> strings, string sep) => String.Join(sep, strings.ToArray());
    public static bool EndsWithOneOf(this string s, params string[] endings) => endings.Any(e => s.EndsWith(e));

    public static IEnumerable<string> Split(this string s, params string[] seps) => s.Split(StringSplitOptions.None, seps);

    public static IEnumerable<string> Split(this string s, StringSplitOptions so, params string[] seps) {
        int pos = 0;
        do {
            var sepPos = seps.Select(sep => new { pos = s.IndexOf(sep, pos) < 0 ? s.Length : s.IndexOf(sep, pos), len = sep.Length }).MinBy(pl => pl.pos);
            if (sepPos.pos > pos || so == StringSplitOptions.None)
                yield return s.Substring(pos, sepPos.pos - pos);
            pos = sepPos.pos + sepPos.len;
        } while (pos <= s.Length);
    }
    public static string FirstWord(this string phrase) => phrase.UpTo(ch => Char.IsWhiteSpace(ch));

    public static bool IsAllLetters(this string s) => s.All(ch => Char.IsLetter(ch)); // faster than Regex
}


public static class DictionaryExt {
    public static TV GetValueOrDefault<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue) => dict.TryGetValue(key, out TV value) ? value : defaultValue;
}

您现在可以创建一些自定义扩展来处理问题的部分内容:

public static class FruitExt {
    public static bool IsPossessive(this string phrase) => phrase.FirstWord().EndsWithOneOf("'s", "'");

    public static string WithIndefiniteArticle(this string phrase) => (phrase.FirstWord().BeginsWithVowelSound() ? "an " : "a ") + phrase;
    public static string ArticleOrPossessive(this string phrase) => phrase.IsPossessive() ? phrase : phrase.WithIndefiniteArticle();
}

现在答案的计算方法与之前相同,但在默认检查元音之前,它会正确处理许多英语语言异常:

var ans = ("I have " + src.Drop(-1).Select(w => w.ArticleOrPossessive()).Join(", ") + " and " + src.Last().ArticleOrPossessive() + ".");

示例输出:

I have an apple, a banana, Papa's citrus, an honest judge, a highchair, a university and an understanding.