JavaScript将字符串解析为整数

5
在Codewars上,我正在尝试解决这个问题:将字符串转换为整数。 这些字符串只是用单词表示的数字。例如:“one” => 1,“twenty” => 20,“two hundred forty-six” => 246,“seven hundred eighty-three thousand nine hundred and nineteen” => 783919。
我想出了下面的代码来实现这一点。点击这里在JSFiddle上查看。我遇到的问题是“七十万”会得到10700这个结果。
我花了一天时间寻找答案,但还是束手无策。程序的步骤如下:
- 将字符串变成'thousand hundred seven',很好。 - 第一个while循环找到“thousand”,并将乘数设置为1000,很好。 - 第二个while循环找到“hundred”,但mult.exec(a[0])语句解析为null,真是糟糕。
因此,乘数不是变成了100000而是值变成了100000,我们注定会得到错误的答案。
在尝试调试时,我尝试在JSFiddle中创建第二个循环中使用的数组a。那里它有效地等于'hundred'而不是null。有人知道为什么会发生这种情况吗?
function parseInt(number) { 

    // reference array for english -> integer
    var ref = { one:1, two:2, three:3, four:4, five:5, six:6, seven:7, eight:8, nine:9, ten:10, eleven:11, twelve:12, thirteen:13, fourteen:14, fifteen:15, sixteen:16, seventeen:17, eighteen:18, nineteen:19, twenty:20, thirty: 30, forty: 40, fifty: 50, sixty: 60, seventy: 70, eighty: 80, ninety:90, hundred: 100, thousand: 1000, million: 1000000 };

    // regex to find number values from the string
    var find = new RegExp( "(one|t(wo|hree|en|welve|hirteen|wenty|hirty)|f(our|ive|ourteen|iftenn|orty|ifty)|s(ixteen|ixty|eventy|ix|even|eventeen|teen)|eigh(ty|t|teen)|nin(ety|e|eteen)|zero|hundred|thousand|million)", "gi" );

    // hundred/thousand/million etc. act as multipliers in this solution and need a seperate search
    var mult = new RegExp( "(hundred|thousand|million)", "gi" );

    // reversing the string allows us to add largest digits first
    number = number.split(' ').reverse().join(" ");

    // while there is a number in string number
    //   if that number is a multiplier
    //     if that number is 100 -> multiplier = multiplier * 100;
    //     else multiplier = reference value;
    //   else value = value + reference value * multiplier
    // end while
    value = 0; multiplier = 1;
    while( a = find.exec(number) ) {

        if( m = mult.exec(a[0]) ) {

            if( m[0] == 'hundred' ) { multiplier *= 100; }
            else { multiplier = ref[m[0]]; }

        }
        else {

            value += ref[a[0]] * multiplier;

        }

    }   
    return value;
}

1
这是一个有趣的问题,你正在处理它,我不能说我完全理解你的代码。然而,其中有一些陷阱与你的问题并不直接相关。1)你可能想避免与内置的“parseInt”函数发生名称冲突。它可能不会影响你的代码行为,但使用内置名称可能是一个不好的习惯。 - Mike Edwards
1
  1. 你的“find”正则表达式没有正确防止匹配适当的前缀 - 在你的JS fiddle示例中,它似乎将“seven”作为适当的前缀进行匹配,而实际上应该匹配“seventeen”。你需要在正则表达式字符串中包含空格保护,以强制进行全词匹配。
- Mike Edwards
1
好主意,我打算将其纳入我的法语词法分析器中,以建议在数字变得太大时使用数字(int)表示而不是单词(string)表示。虽然我同意@MikeEdwards的观点-有一些情况需要注意:7将是sevenseventeen的结果,因为正则表达式在找到第一个有效部分(seven)时停止匹配。 fourfourteen中的4也是另一个例子。您需要像他建议的那样的解决方法。此外,有趣的是,英语中的每个数字都可以分解成30多个部分。 - Chris Cirefice
我应该澄清一下 - 我所说的 'this' 并不是指 你的代码。我指的是将法语文本中的字面数字表示解析为整数表示的想法,以便在字面表示 "太长" 时提供建议。我回顾了一下,意识到自己措辞有些不准确,并且错过了修改截止日期 :) - Chris Cirefice
在JavaScript中,除非您的正则表达式是动态的,否则应始终使用正则表达式字面量。这些似乎不是... - Ry-
4个回答

5
也许你不需要正则表达式。
function parse(numbersInString){
    var ref = { one:1, two:2, three:3, four:4, five:5, six:6, seven:7, eight:8, nine:9, ten:10, eleven:11, twelve:12, thirteen:13, fourteen:14, fifteen:15, sixteen:16, seventeen:17, eighteen:18, nineteen:19, twenty:20, thirty: 30, forty: 40, fifty: 50, sixty: 60, seventy: 70, eighty: 80, ninety:90 },
        mult = { hundred: 100, thousand: 1000, million: 1000000 },
        strNums = numbersInString.split(' ').reverse(),
        number = 0,
        multiplier = 1;

    for(i in strNums){
        if( mult[strNums[i]] != undefined ) {
            if(mult[strNums[i]]==100) {
                multiplier*=mult[strNums[i]]
            }else{
                multiplier=mult[strNums[i]]
            }
        } else {
            if (!isNaN(parseFloat(strNums[i]))) {
                number += parseFloat(strNums[i]) * multiplier;
            } else {
                var nums = strNums[i].split('-');
                number += ((ref[nums[0]]||0) + (ref[nums[1]]||0)) * multiplier;
            }
        }
    }
    return number;
}

这就是我把问题想得比实际要难的完美例子。非常感谢你,Grundy。 - benwsmith

1

感觉应该把 mult.exec 放在一个 while 块中,以便将所有乘数组合在一起。

这个小片段

while( a = find.exec(number) ) {
    if( m = mult.exec(a[0]) ) {
        while(m) {
            multiplier *= ref[m[0]];
            m = mult.exec(a[0]);
        }
    }
    else {
        value += ref[a[0]] * multiplier;
    }
}   

使事情对七十万人起作用,但对那么多人崩溃了。百位数在number数组和multiplier数组中的事实可能是问题的根源,尽管我找不到确切的解决方案。
有趣的小问题。也许有了这个线索,你就可以解决剩下的问题了。

1
非常有趣的问题。我认为观察字符串中是否出现“百”,“万”,“亿”等词语非常重要,它们可以表示前面所有内容乘以这个数字,也可以表示这个数字本身。如果是“百”,可能这还不是结尾。我们可能需要在之后再乘以另一个数量级,比如“十万”。
因此,我将总数的计算分成了三个变量:totalOfUnitstotalOfHundredstotalOfMultitudes。然后按顺序修订所有数字,如下所示:
  • 如果遇到小于100的数字,将其加入totalOfUnits
  • 如果遇到100:
    • 如果totalOfUnits == 0,则将100添加到totalOfHundreds
    • 如果totalOfUnits > 0,则将100 * units添加到totalOfHundreds,并将totalOfUnits设置为0
  • 如果遇到其他倍数:
    • 如果totalOfUnits == 0且百位数== 0,则将倍数本身添加到totalOfMultitudes
    • 如果totalOfUnits > 0或totalOfHundreds > 0,则将倍数乘以(totalOfUnits + totalOfHundreds)添加到totalOfMultitudes,并将totalOfUnitstotalOfHundreds设置为0。
最后,返回totalOfUnits + totalOfHundreds + totalOfMultitudes。该代码适用于所有给定的示例,代码如下:
function parseNumber(num){
    var units = {
        zero:0, one:1, two:2, three:3, four:4, five:5, six:6, seven:7, eight:8, nine:9, ten:10,
        eleven:11, twelve:12, thirteen:13, fourteen:14, fifteen:15, sixteen:16, seventeen:17, eighteen:18, nineteen:19,
        twenty:20, thirty: 30, forty: 40, fifty: 50, sixty: 60, seventy: 70, eighty: 80, ninety:90
    };
    var hundreds = {
        hundred: 100
    };
    var multitudes = {
        hundred: 100,
        thousand: 1000,
        million: 1000000
    };

    var parts = num.split(/[ -]/);

    totalOfUnits = 0;
    totalOfHundreds = 0;
    totalOfMultitudes = 0;

    var result = undefined;

    for(var i = 0; i < parts.length; i++){
        if(units[parts[i]]){
            //Add unit
            totalOfUnits = totalOfUnits + units[parts[i]];
        }else if(hundreds[parts[i]]){
            totalOfHundreds += hundreds[parts[i]] * (totalOfUnits || 1);
            totalOfUnits = 0;
        }else if(multitudes[parts[i]]){
            totalOfMultitudes += multitudes[parts[i]] * (((totalOfUnits || 0) + (totalOfHundreds || 0)) || 1);
            totalOfUnits = totalOfHundreds = 0;
        }
    }

    return totalOfUnits + totalOfHundreds + totalOfMultitudes;
}

谢谢你的建议,Borre。我喜欢将其分为三种不同类型而不是只有两种的想法。干杯。 - benwsmith

-1

这不是一个答案,但我想对这种方法进行一些评论,因为我不认为您可以使用当前的算法解析单词。您可能会使其在某个特定实例或范围内工作,但它永远不会适用于所有单词变体。

如果您将所有类型的单位分别计数到数据结构中,那么这将更容易和更可预测,例如:

var data = {
    millions:3, 
    hundredThousands:2,
    tenThousands:6, 
    thousands:6, 
    hundreds:0, 
    tens:8, 
    ones:9
};

在计算完成后,您可以简单地将所有结果(包括零)串联起来以组成整数值。
var value = [
        data.millions,
        data.hundredThousands,
        data.tenThousands,
        data.thousands,
        data.hundreds,
        data.tens,
        data.ones
    ].join('');

return parseInt(value); // 3266089

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接