数字转换文本_.NET数字到文本转换器

数字转换文本

More often than not, we developers are confronted with a need: a need to make some kind of magic happen via code. Whether it is for a client, for the boss, or for our own personal projects, the need must be satisfied. Most of the time, the Framework satisfies such needs out of the box; sometimes, however, we must craft our own magical creation in order to satiate our need. To my knowledge, one such feature that is lacking from the Framework prior to removing the shrink wrap is that of converting a numeric value to a word representation. The uses for such a device are varied, and probably slightly niche, yet the need exists. In this article, I am going to demonstrate one approach to creating logic which will fulfill this need.

我们的开发人员经常会遇到一种需求:通过代码实现某种魔术的需求。 无论是客户,老板还是我们自己的个人项目,都必须满足需求。 在大多数情况下,框架可以立即满足此类需求。 但是,有时候,我们必须精心制作自己的魔术作品才能满足我们的需求。 据我所知,在删除收缩包装之前,框架缺少的一个这样的功能是将数字值转换为单词表示的功能。 这种装置的用途是多种多样的,并且可能略微利基,但是仍然存在需求。 在本文中,我将演示一种创建满足此需求的逻辑的方法。

The code snippets in this article will be in C#, but I am including both a C# and VB.NET version of this code as attachments. You can find links to these attachments at the bottom of the article. My goal is that the article be clear enough for the novice of either language to follow along. Do not be shy about opening up the VB version if you have trouble following the code.

本文中的代码段将使用C#,但是我同时将此代码的C#和VB.NET版本都包含在附件中。 您可以在文章底部找到这些附件的链接。 我的目标是使这篇文章足够清晰,以使任何一种语言的新手都可以理解。 如果您在遵循代码方面遇到困难,请不要羞于打开VB版本。

To make sure everyone is on the same page, let's demonstrate what I mean by "numbers to words". Let's say you have the following numeric value:

为了确保每个人都在同一页面上,让我们演示一下“数字到单词”的含义。 假设您有以下数值:

23,456,789
23,456,789

That is, twenty-three million four hundred fifty-six thousand seven hundred eighty-nine. So what does the conversion to words look like? Well, it is two sentences back! The need in question is taking the familiar Arabic numeral representation of a number and converting it to a series of words which evokes the same concept of quantity. Let me take a look at how the process could broken up.

就是说,两亿三千四百五十六万七千八百九十九。 那么转换为单词是什么样的呢? 好吧,这是两句话! 所需要的是采用熟悉的阿拉伯数字表示形式并将其转换为唤起相同数量概念的一系列单词。 让我看看该过程如何分解。

Before I get into the guts of the logic, ask yourself this: what makes up a number? In other words, what are the parts that make a number--both the high-level and low-level parts. For this project, I looked at numbers in this manner: a number is set of digits, grouped by threes, going from right-to-left. You can see this partly in the number I mentioned earlier: the entire number is a set of digits, the digits are grouped into threes (delimited by commas), and the groups are packed from right-to-left since the left-hand side of the number only has two digits. Each group has common features that it shares with the other groups; each also has unique features. The common features include a spot for hundreds, tens, and ones positions. The unique feature each group has is that its multiplier increases by a factor of 1000 as you move from group to group, going from right-to-left. Taking all of this into account, developing a system of turning numbers into words should not be that difficult. Now that I have visualized the parts, let me conceptualize the code to manipulate those parts.

在深入探讨逻辑之前,请先问自己:什么构成数字? 换句话说,构成数字的部分是什么—高层部分和底层部分。 在本项目中,我以这种方式查看数字:数字是一组数字,从右到左按三位分组。 您可以在我前面提到的数字中部分看到这一点:整个数字是一组数字,这些数字被分为三部分(用逗号分隔),并且各组从左到右从右到左排列的数字只有两位数字。 每个组都具有与其他组共享的共同特征。 每个也都有独特的功能。 常见功能包括一个用于数百个,数十个和一个位置的位置。 每个组具有的独特功能是,当您从一个组移动到另一个组时(从右到左),其乘数将增加1000倍。 考虑到所有这些,开发将数字转换为单词的系统应该不会那么困难。 现在,我已经可视化了零件,现在让我概念化代码以操纵那些零件。

获取数字文字 (Getting the Text of a Number)

让我从一个名为 GetNumberText. I will use this function to return our newly created string. You'll most likely want to check for non-numbers before you get too far into the function call-wise, but for now, assume I have a valid number.
Note: I am using a regular expression here to remove the non-digit characters from the initial string. I am comfortable with regular expressions, so this is trivial for me. You can change this to some other logic if you prefer to avoid regular expressions, but the logic to evaluate the string depends on a string consisting of only digits, so ensure your string is composed as such.

In this case, the "\D" means anyhting that is NOT a digit--here, case is important ("\d" means any digit) and there are two backslashes due to C#'s handling of backslashes within strings. Since I am doing a Replace call, anything that matches the pattern will be replaced with the replacement value--in this case, an empty string. The net effect is that all non-digit characters are removed in the resulting string.

注意:

在这种情况下,“ \ D”表示不是数字的任何字符-在此情况下,大小写很重要(“ \ d”表示任何数字),由于C#对字符串中的反斜杠进行了处理,因此存在两个反斜杠。 由于我正在执行Replace调用,因此与模式匹配的所有内容都将替换为替换值-在这种情况下为空字符串。 最终结果是在结果字符串中删除了所有非数字字符。

Earlier I suggested that a number is a set of groups (subsets). Our first step is to break the number into its respective groups. For example, I could generate an array of groups in this manner:

之前我建议数字是一组组(子集)。 我们的第一步是将数字分成各自的组。 例如,我可以用这种方式生成一组数组:

string[] parts = SplitValue(value);
private static string[] SplitValue(string value)
{
    string[] parts;
    string leftMost = null;
    int numParts = value.Length / 3;

    if (value.Length % 3 != 0)
    {
        leftMost = value.Substring(0, value.Length - (numParts * 3));
        numParts++;
    }

    parts = new string[numParts];

    for (int i = value.Length - 3, j = numParts - 1; j > 0; i -= 3, j--)
    {
        parts[j] = value.Substring(i, 3);
    }

    parts[0] = (leftMost ?? value.Substring(0, 3));

    return parts;
}

In the above, I am determining the number of groups using a combination of integer division and a modulus operation. The integer division gives the number of full groups (groups will be considered to always be of length 3, except possibly the first--left-most--group). The modulus operation tells whether or not a partial group exists. I piggy-back onto this operation and get the text for the partial group also. Once the number of groups is determined, I loop through the string and create each group as a separate slot in the returned array. The first slot in the array will hold the partial value if it exists; otherwise, it will be a full group. Once the array has been filled, I return back to GetNumberText.

在上面,我正在使用整数除法和模运算的组合来确定组数。 整数除法给出了完整组的数量(除第一个(最左边)组外,其他组的长度始终为3)。 模运算表明是否存在部分组。 我背负着此操作,还获得了部分组的文本。 确定组数后,我将遍历字符串并将每个组创建为返回数组中的单独插槽。 数组中的第一个插槽将保留部分值(如果存在); 否则,它将是一个完整的小组。 数组填满后,我将返回到

Back within GetNumberText, I check to see how many groups were created, and based on that value, I make the appropriate calls to create each group's text:

回到

if (parts.Length > 4) throw new System.OverflowException("Recieved value is too large.");
if (parts.Length > 3) GetGroup(parts[i++], BILLIONS, result);
if (parts.Length > 2) GetGroup(parts[i++], MILLIONS, result);
if (parts.Length > 1) GetGroup(parts[i++], THOUSAND, result);

GetGroup(parts[i], string.Empty, result);

As you can see, the higher the group count goes, the more "-illions" I process. If I wanted to add even higher values, then hopefully all I should have to do is change the overflow condition, and add a new if and constant for each -illion I wan to be able to process. All that is left for the GetNumberText function is to return the calculated value. "Where does that aforementioned 'magic' happen," you say? That, my friend, occurs inside the GetGroup method. Let us examine that method.

如您所见,组数越高,我处理的“百万”就越多。 如果我想添加更高的值,那么希望我要做的就是更改溢出条件,并为要处理的每一百万添加一个新的if和常数。

private static void GetGroup(string value, string denomination, StringBuilder result)
{
    char[] reversed = Reverse(value);

    if (value.Length > 2 && value[0] != ZERO)
    {
        result.Append(GetDigit(reversed[2]));
        result.Append(HUNDREDS);
    }

    if (reversed.Length > 1)
    {
        if (reversed[1] > ONE)
        {
            result.Append(GetTens(reversed[1]));

            if (reversed[0] != ZERO)
            {
                result.Append('-');
            }
        }
        else if (reversed[1] == ONE && reversed[0] == ZERO)
        {
            result.Append(GetTens(reversed[1]));
        }
        else if (reversed[1] != ZERO)
        {
            result.Append(GetTeens(reversed[0]));
        }
    }

    if (reversed.Length > 1)
    {
        if (reversed[0] > ZERO && reversed[1] != ONE)
        {
            result.Append(GetDigit(reversed[0]));
        }
    }
    else
    {
        result.Append(GetDigit(reversed[0]));
    }

    result.Append(denomination);
}

In the GetGroup method, I start by reversing the order of the digits. This is only to make the subsequent if logic a bit simpler. I essentially check the length of the current group, and based on that length, I append the appropriate identifier. You will notice that the if for the hundreds is a bit shorter than the subsequent checks for the tens or the ones. This is because identifiers for the hundreds place are straightforward; the identifiers for tens and ones are a bit more complicated. If I have something in the tens position, I have to check whether or not I am dealing with one of the "-ty" values (e.g. twenty, thirty, etc.) or if I am dealing with a "-teen" value (e.g. thirteen, fourteen, etc.). Even though they do not end in "teen", I include eleven and twelve as "teen" values. Because of the irregularities which occur in the teens, namely eleven and twelve, our logic becomes a bit more involved.

,如果逻辑简单一点以后。 我基本上检查当前组的长度,然后根据该长度添加适当的标识符。 您会注意到,数百个的if比随后的十个或多个check短一些。 这是因为数百个地方的标识符很简单; 十和一的标识符要复杂一些。 如果我的东西处于十位,则必须检查是否正在处理“ -ty”值之一(例如二十,三十等),或者是否正在处理“十岁”值(例如13、14等)。 即使它们不以“ teen”结尾,我还是将11和12作为“ teen”值。 由于青少年中出现的不规则现象(即十一岁和十二岁),我们的逻辑变得更加复杂。

To check the hundreds position, I need to ensure that I have three digits and that the value in that position is not zero. It is fine if the value is zero, but I do not want to append an identifier if it is. There is not a concept of "zero hundred" that I am familiar with.

要检查百位,我需要确保我有三位数字,并且该位置的值不为零。 如果该值为零是可以的,但是我不想附加一个标识符。 我不熟悉“零百”的概念。

Here is how our tens check (i.e. the second if block) breaks down. I start by checking that I have at least two digits in the incoming value. If so, I first look for a tens value that is greater than one. This tells me that I have one of the "-ty" values. I get request the word equivalent of this value, then I check whether or not the ones value is a zero. If it is, then our word is done; if it is not, then I add a hyphen to separate the tens and subsequent ones value. Now, if our tens value is not greater than one, I need to check whether it is exactly one--because then I have a teen value. If the value of the ones position is zero, then the value is ten exactly, and I get the word equivalent of it; otherwise, I get the word equivalent of a teen value. For this teen, I pass the value of the ones column, since I already know that the tens column is a one. Reaching the end of this block, however, does not indicate that I am finished, even if I acquired a teen value.

这是我们的十进制检查(即第二个if块)分解的方式。 我首先检查输入值中是否至少有两位数。 如果是这样,我首先寻找一个大于1的十进制值。 这告诉我,我具有“ -ty”值之一。 我得到请求该值的单词等效,然后检查ones值是否为零。 如果是这样,那么我们的话就完成了; 如果不是,那么我添加一个连字符来分隔tens和随后的值。 现在,如果我们的tens值不大于1,我需要检查它是否恰好是一个,因为那我的价值很小。 如果“ ones”位置的值为零,则该值正好为“十”,我得到与之等效的单词; 否则,我得到相当于青少年价值的单词。 对于这个青少年,我传递了one列的值,因为我已经知道tens列是一个。 但是,即使我获得了青少年的价值,到达本步骤的结尾也并不表示我已经完成。

For the ones check, our third if, I need to confirm a couple of things:

对于那些检查,我们的第三个if ,我需要确认以下几点:

2) If the length is greater than one, is the value of the tens column not equal to one or is the value of the ones column greater than zero

2)如果长度大于一,则tens列的值不等于一,还是一列的值大于零

The reasoning for the above logic is as follows. If the length of the incoming value is greater than one, I have a value for the tens column. As such, I need to check what kind of value, a "-ty" or a "-teen" value, was generated. If I generated a "-ty" value, then I have to make sure that I didnt' generated one of the multiples of ten (e.g. twenty, thirty, forty, etc.). If I did, then I won't be appending any words to the end of the value. There is no such value as "twenty-zero". If instead I generated a teen, I don't want to append a word either. Again, there is no such value as "nineteen-nine". If I can confirm, via the inner if that neither of these is the case, then I get the value of the ones column and append it to the result. That handles our ones column where I also have a tens column. For values where I only have a ones column (i.e. the incoming value is of length one), I simply call the function to return the word equivalent of the incoming value. This is handled by the else block.

上述逻辑的理由如下。 如果输入值的长度大于一,则为tens列提供一个值。 因此,我需要检查生成了哪种类型的值,即“ -ty”或“ -teen”值。 如果我生成了“ -ty”值,则必须确保没有生成十的倍数之一(例如,二十,三十,四十等)。 如果我这样做了,那么我将不会在值的末尾附加任何单词。 没有“二十零”这样的值。 相反,如果我生了一个十几岁的孩子,我也不想添加任何单词。 同样,不存在“十九九”这样的值。 如果我可以通过内部确认是否都不是,那么我将获得一列的值并将其附加到结果中。 那处理我们的一列,我也有一个十列。 对于只有一列(即输入值的长度为1)的值,我只需调用函数以返回与输入值等效的词。 这由else块处理。

As I mentioned earlier, the GetGroup method does much of the work in this process. The other functions it calls, GetDigit, GetTens, and GetTeens, are really just dumb functions that receive an incoming character and return its equivalent word, based on the context in which I choose a function and call it. Having these functions, however, gives me flexibility and code reuse, so I have not complaints to their dumbness.

如前所述,

获取数字的序数文本 (Getting the Ordinal Text of a Number)

我最初是为了回答我参与的一个问题而开始考虑这个整体概念。这个问题实际上是在要求返回数字的序数文本。 对于那些对这个特定实用程序感兴趣的人来说,好消息是我完成了上面的大部分工作。 唯一需要调整的是前面描述的逻辑生成的数字的后缀。 在我介绍这些内容之前,让我们刷新一下大家对序数是什么的概念。

At one point or another, we have all participated in some form of contest. Whether it be a spelling bee, a programming competition, or a scholastic examination. When you have the concept of a contest, you implicitly have the concept of a position in which each individual completes said contest. This is what an ordinal is: it describes the position of some thing in a given context. In the context of a competition, you have the first-place winner, the second-place winner, etc. The same holds true for other forms of contest. For a given number, to get its ordinal value a suffix is appended to the name. In some cases the name of the base number must be modified to some variant of the original (e.g. "twelve" becomes "twelfth"). Here is what the code to do so could look like.

在某一时刻,我们都参加了某种形式的比赛。 无论是拼字比赛,编程比赛还是学术考试。 当您具有竞赛的概念时,您就隐含了每个人都完成所述竞赛的职位的概念。 这就是序数是什么:它描述了给定上下文中某事物的位置。 在比赛中,您拥有第一名,第二名等。其他形式的比赛也是如此。 对于给定的数字,要获得其序数值,必须在名称后附加一个后缀。 在某些情况下,必须将基本编号的名称修改为原始名称的某些变体(例如,“十二”变成“第十二”)。 这样做的代码如下所示。

As mentioned above, most of the hard work has been covered by the discussion above. And as I just informed you, to get an ordinal, I simply need to append a suffix, or I need to modify the base word appropriately for those special occurrences. For the purposes of ordinality, the suffixes involved will be "st", "nd", "rd", and "th". Most numbers belong to a group which uses the "th" suffix. Here is the function I concocted to adjust the phrase generated by GetNumberText to include the suffix:

如上所述,上面的讨论已经涵盖了大多数艰苦的工作。 而且,正如我刚刚告诉您的那样,要获得序数,我只需要添加一个后缀,或者我需要针对那些特殊情况适当地修改基本词。 为了普通起见,所涉及的后缀将为“ st”,“ nd”,“ rd”和“ th”。 大多数数字属于使用“ th”后缀的组。 这是我构想的功能,用于调整由

public static string GetOrdinalText(string value)
{
    if (value.Length == 0) return value;

    StringBuilder result = new StringBuilder(GetNumberText(value));

    if (value.Length == 1)
    {
        value = "x" + value;
    }

    if (result[result.Length - 1] == 'y')
    {
        result.Length--;
        result.AppendFormat("ie{0}", GetSuffix(value[value.Length - 1]));
    }
    else if (value[value.Length - 1] == ONE && value[value.Length - 2] != ONE)
    {
        result.Length -= 3;
        result.AppendFormat("Fir{0}", GetSuffix(value[value.Length - 1]));
    }
    else if (value[value.Length - 1] == TWO && value[value.Length - 2] != ONE)
    {
        result.Length -= 3;
        result.AppendFormat("Seco{0}", GetSuffix(value[value.Length - 1]));
    }
    else if (value[value.Length - 1] == THREE && value[value.Length - 2] != ONE)
    {
        result.Length -= 5;
        result.AppendFormat("Thi{0}", GetSuffix(value[value.Length - 1]));
    }
    else if (result[result.Length - 1] == 'e' || result[result.Length - 1] == 't')
    {
        result.Length--;

        if (result[result.Length - 1] == 'v')
        {
            result[result.Length - 1] = 'f';
        }

        if (value[value.Length - 1] == TWO)
        {
            result.Append(GetSuffix(value[value.Length - 1], true));
        }
        else
        {
            result.Append(GetSuffix(value[value.Length - 1]));
        }
    }
    else
    {
        if (value[value.Length - 2] == ONE)
        {
            result.Append(GetSuffix(value[value.Length - 1], true));
        }
        else
        {
            result.Append(GetSuffix(value[value.Length - 1]));
        }
    }

    return result.ToString().Trim();
}

Firstly, I create the word equivalent of the number by making a call to the earlier described GetNumberText method. This is the base for our modifications. I will only be modifying the last word in the phrase, so a StringBuilder should grant an simple interface to do so. You'll notice I have included a small bit to append an "x" to the value. The "x" is really arbitrary, and the reason I included it was for times when a single value is being evaluated. Making the value be of length two rather than leaving it length one allowed me to escape a more complicated if block later. I will describe where this came into play soon. After this small adjustment, I start examining the last word in our phrase.

首先,我通过调用前面描述的,如果以后块。 我将描述这很快就会发挥作用的地方。 经过小小的调整后,我开始检查短语中的最后一个单词。

The first check comes in the form of looking for a trailing "y" on our word string. If I find such a character, then I have one of the multiples of ten. Multiples of ten use the "th" suffix, and they change their "y" to an "ie". Hence the first if block. If I do not find a "y" in the trailing position, I move on to the next condition.

第一步检查是在我们的单词串上寻找结尾的“ y”。 如果找到这样的角色,那么我就是十的倍数之一。 十的倍数使用“ th”后缀,并且将其“ y”更改为“ ie”。 因此,第一个if块。 如果在尾随位置找不到“ y”,则继续进行下一个条件。

The subsequent else if block checks the original numeric value for a trailing "1" that is not preceded by a "1". In other words, I am looking for a value that is not "11" in the right-most positions of the original numeric value. Here, and the following two else if blocks, is where the appending of the arbitrary value mentioned earlier comes into play. The first part of each of the three else if blocks will succeed no matter what, since I am guaranteed to have at least one character. If, however, I only have one character (sans append), then the second part of each else if would throw an index-out-of-bounds exception. So I sacrifice a call to append to save handling an exception. If both conditions match, then I shorten the length of the string by the length of the word that is at the end of the phrase--in this case, "One". I then append the equivalent ordinal phrase to the result--in this case "First". Notice that I am still calling the GetSuffix method in the else if body. I certainly could append the value "First" rather than append "Fir" followed by the call to GetSuffix, but I wanted to the logic similar throughout. This also grants me that if the suffixes ever change, I only need to change the GetSuffix method internally, and change the "Fir" if the base of the ordinal value changed. The two else if blocks following the first operate in the same manner, but evaluating two and three, respectively.

后续的else if块将检查原始数字值是否为尾随的“ 1”,而不是后跟“ 1”。 换句话说,我正在寻找原始数字值最右边不为“ 11”的值。 在这里以及后面的两个其他if块中,前面提到的任意值的附加起作用。 其他三个if块的第一部分无论如何都会成功,因为我保证至少要有一个字符。 但是,如果我只有一个字符(不带追加),则其他字符的第二部分将抛出index-out-of-bounds异常。 因此,我牺牲了一个附加请求来保存对异常的处理。 如果两个条件都匹配,则我将字符串的长度缩短为短语结尾处的单词的长度(在本例中为“一个”)。 然后,我在结果中附加等效的序数词组-在本例中为“ First”。 注意,我仍然在else if主体中调用if块以相同的方式运行,但是分别求值两个和三个。

Since I mentioned it above, what's the deal with GetSuffix anyway? Well, it's another dumb function that just returns a suffix based on the incoming digit value. Here's what it looks like:

既然我在上面提到过,

private static string GetSuffix(char value, bool treatAsTeen)
{
    const string TH = "th";

    switch (value)
    {
        case ONE:
            return treatAsTeen ? TH : "st";
        case TWO:
            return treatAsTeen ? TH : "nd";
        case THREE:
            return treatAsTeen ? TH : "rd";
        case FOUR:
        case FIVE:
        case SIX:
        case SEVEN:
        case EIGHT:
        case NINE:
        case ZERO:
            return TH;
        default:
            return string.Empty;
    }
}

private static string GetSuffix(char value)
{
    return GetSuffix(value, false);
}

Yes, it is an overloaded function, and I will explain shortly why I overloaded it. As you can see, in the overload that does most of the work, I have a second parameter of type bool. The "treatAsTeen" variable is used to indicate when an incoming "1", "2", or "3" should be considered to represent eleven, twelve, or thirteen, respectively. The reason is that one has a different suffix than eleven does. The same is true for the other 4 numbers. Again, I tolerate this function's dumbness because it simplifies our goal. Now, back to parsing...

是的,它是一个重载函数,稍后我将解释为什么我将其重载。 如您所见,在完成大部分工作的重载中,我有第二个参数,类型为bool 。 “ treatAsTeen”变量用于指示何时应认为传入的“ 1”,“ 2”或“ 3”分别代表11、12或13。 原因是后缀不同于11。 其他4个数字也是如此。 同样,我可以容忍此功能的愚蠢之处,因为它简化了我们的目标。 现在,回到解析...

The else if block preceding the else block is used to check for words that end in either "e" or "t". The reason I do so is that these letters are dropped in the ordinal equivalent's representation. I also have an internal if block to check for a "v". For words that fit this characteristic, the "v" is changed to an "f". A word like "five" becomes "fifth" in the ordinal. The remaining if block checks for a ones value of "2" in the original numeric value. If I find a "2", then I must be working with the word "twelve", since the "e" check brought me into this else if. Since I am working with "twelve", I need to call the overloaded version of GetSuffix, telling it that I am passing it a "teen" word. This ensures I get the appropriate suffix for twelve. If I do not find a "2" in the ones position, then I can simply call GetSuffix with one parameter. This leaves me with only one case yet to examine.

else块之前的else if块用于检查以“ e”或“ t”结尾的单词。 我这样做的原因是这些字母被删除在等效顺序的表示形式中。 我也有一个内部if块来检查“ v”。 对于符合此特征的单词,“ v”更改为“ f”。 像“五”这样的词在序数中变成“第五”。 其余的if块将检查原始数值中的“ 1”值。 如果我找到“ 2”,那么我必须使用“十二”一词,因为“ e”检查使我进入了if 。 因为我使用的是“十二”,所以我需要调用

In the final else block, I check whether or not the tens position is a one. Remember, I appended an arbitrary value if the original value was of length one, so I should not get an out-of-bounds exception here even if I do have but one number to process. If the value is one, then I must be working with a teen, and so I need to call the teen version of the GetSuffix method. Otherwise, I can simply call the single-parameter version and be done.

在最后的else块中,我检查tens位置是否为1。 请记住,如果原始值的长度为1,我会附加一个任意值,因此即使我只需要处理一个数字,也不会在这里出现越界异常。 如果值为1,那么我必须与十几岁的孩子一起工作,因此我需要调用

摘要 (Summary)

这样就可以了:创建数字到文本转换器的几个简单步骤。 那不是那么痛苦,不是吗? 同样,这可能不是您每天都可以使用的东西,但是如果您需要模拟合法的检查路线,在比赛中阐明优胜者的位置,或者具有其他创造性的用途来显示文字数字,那么现在这样做的实用程序。 您甚至可以修改代码以使用不同的语言,尽管您可能需要调整后缀的应用方式。 如果您能够在项目中使用此处描述的代码,请随时给我留言以表示您的经验。 我总是很高兴知道我的产品何时能够帮助某个人摆脱困境。 如果我看到代码方面有待改进,甚至可以发表评论,尽管我的重点不是严格的性能。 现在,我说:“谢谢您的阅读!”

代码链接 (Code Links)

C# Code File C#代码文件 VB.NET Code File VB.NET代码文件

翻译自: https://www.experts-exchange.com/articles/7270/NET-Number-to-Text-Converter.html

数字转换文本

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现对多个声卡同时输出文本语音,您可以使用 .NET Framework 中的 System.Speech 命名空间来实现。以下是一个示例代码,演示如何使用 System.Speech 命名空间来将一段文本转换为语音,并将其输出到多个声卡中: ```csharp using System; using System.Collections.Generic; using System.Linq; using System.Speech.Synthesis; namespace SpeechSynthesis { class Program { static void Main(string[] args) { // 获取计算机上所有的音频输出设备 var outputDevices = new List<SynthesizerDevice>(); foreach (var device in SpeechSynthesizer.GetInstalledVoices().Select(v => v.VoiceInfo.Synthesizer)) { if (!outputDevices.Any(d => d.Id == device.Id)) { outputDevices.Add(device); } } // 设置语音合成器 var synthesizer = new SpeechSynthesizer(); synthesizer.SetOutputToDefaultAudioDevice(); // 将语音合成器的输出设置为默认音频设备 // 将文本转换为语音,并输出到每个音频设备中 foreach (var device in outputDevices) { synthesizer.SetOutputToAudioStream(device.AudioOutputStream, new SpeechAudioFormatInfo(16000, AudioBitsPerSample.Sixteen, AudioChannel.Stereo)); // 将语音合成器的输出设置为当前设备的音频流 synthesizer.Speak("Hello World!"); // 将文本转换为语音并输出到当前设备 } Console.ReadKey(); } } } ``` 以上代码将获取计算机上所有的音频输出设备,并将一段文本转换为语音,然后将其输出到每个音频设备中。您可以根据您的需求更改文本内容、音频格式等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值