如何检查一个字符串是否是有效的数字?

1911

我希望有与旧的VB6 IsNumeric()函数在同一概念空间中的东西?


5
请参考我之前提出的相关问题此链接 - Michael Haren
55
如果您查看这个问题,请尽量跳过所有正则表达式的答案。那不是解决问题的正确方式。 - Joel Coehoorn
18
除非有人想要做这件事:检查给定字符串是否具有有效数字流的格式。那么为什么这样做是错的呢? - SasQ
41
选定的答案是不正确的!!!请查看其评论,但基本上它在例如isNaN("")isNaN(" ")isNaN(false)等情况下失败了。它返回false,这意味着它们是数字。 - Andrew
11
所选答案是不正确的,正则表达式也不能解决这个问题。那么正确的方法是什么? - vir us
显示剩余3条评论
53个回答

3174
2020年10月2日:请注意,许多简单的方法存在微妙的错误(例如空格、隐式部分解析、基数、数组强制转换等),而这些答案中许多都没有考虑到。以下实现可能适用于您,但请注意它不适用于除小数点“.”以外的数字分隔符。
function isNumeric(str) {
  if (typeof str != "string") return false // we only process strings!  
  return !isNaN(str) && // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)...
         !isNaN(parseFloat(str)) // ...and ensure strings of whitespace fail
}

检查变量(包括字符串)是否为数字,请检查它是否不是数字:

无论变量内容是字符串还是数字,此方法都适用。

isNaN(num)         // returns true if the variable does NOT contain a valid number

示例

isNaN(123)         // false
isNaN('123')       // false
isNaN('1e10000')   // false (This translates to Infinity, which is a number)
isNaN('foo')       // true
isNaN('10px')      // true
isNaN('')          // false
isNaN(' ')         // false
isNaN(false)       // false

当然,如果需要的话,您可以对此进行否定。例如,要实现您给出的 IsNumeric 示例:

function isNumeric(num){
  return !isNaN(num)
}

将包含数字的字符串转换为数字:

仅当字符串包含数字字符时才有效,否则它将返回NaN

+num               // returns the numeric value of the string, or NaN 
                   // if the string isn't purely numeric characters

例子

+'12'              // 12
+'12.'             // 12
+'12..'            // NaN
+'.12'             // 0.12
+'..12'            // NaN
+'foo'             // NaN
+'12px'            // NaN

将字符串松散地转换为数字

例如,将'12px'转换为12十分有用:

parseInt(num)      // extracts a numeric value from the 
                   // start of the string, or NaN.

例子

parseInt('12')     // 12
parseInt('aaa')    // NaN
parseInt('12px')   // 12
parseInt('foo2')   // NaN      These last three may
parseInt('12a5')   // 12       be different from what
parseInt('0x10')   // 16       you expected to see.

浮点数

请注意,与+num不同,parseInt(顾名思义)将通过截断小数点后的所有内容来将浮点数转换为整数(如果您想使用parseInt()因为这种行为,您最好使用其他方法):

+'12.345'          // 12.345
parseInt(12.345)   // 12
parseInt('12.345') // 12

空字符串

空字符串可能有点让人感到迷惑。 +num 会将空字符串或带有空格的字符串转换为零,而 isNaN() 也会做出相同的假设:

+''                // 0
+'   '             // 0
isNaN('')          // false
isNaN('   ')       // false

但是parseInt()并不同意:

parseInt('')       // NaN
parseInt('   ')    // NaN

177
关于parseInt的一个非常重要的注意事项是,它允许您指定一个基数来将字符串转换为整数。这是一个需要谨慎的问题,因为如果您没有提供基数,它将尝试为您猜测一个基数。例如,parseInt("17") 的结果是 17(十进制,10),但 parseInt("08") 的结果是 0(八进制,8)。因此,除非您有意不同,否则最安全的方法是使用parseInt(number, 10),显式地指定10作为基数。 - Adam Raney
49
注意!!isNaN(undefined) 返回false。 - David Hellsing
172
这完全是错误的,它怎么获得那么多点赞?你不能使用 isNaN 来“检查一个变量是否不是数字”。“不是数字”并不等于“IEEE-794 NaN”,而这正是 isNaN 进行测试的内容。特别是在测试布尔值和空字符串时,这种用法会失败。请参见 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/isNaN#Description 。 - EML
72
检查某个值是否为数字的最快方式是使用“自等检查”:var n = 'a'; if (+n === +n) { // is number } 在最新版本的Chrome中,它比isNaN快约3994%。在这里查看性能测试:http://jsperf.com/isnan-vs-typeof/5 - Kevin Jurkowski
32
注意:这个答案是错误的。使用时自行承担风险。示例:isNaN(1 + false + parseInt("1.do you trust your users?"))请问你信任你的用户吗? - keithpjolley
显示剩余28条评论

199

如果您只是想检查字符串是否为整数(没有小数),则正则表达式是一个不错的选择。其他方法,例如isNaN对于如此简单的事情来说太复杂了。

function isNumeric(value) {
    return /^-?\d+$/.test(value);
}

console.log(isNumeric('abcd'));         // false
console.log(isNumeric('123a'));         // false
console.log(isNumeric('1'));            // true
console.log(isNumeric('1234567890'));   // true
console.log(isNumeric('-23'));          // true
console.log(isNumeric(1234));           // true
console.log(isNumeric(1234n));          // true
console.log(isNumeric('123.4'));        // false
console.log(isNumeric(''));             // false
console.log(isNumeric(undefined));      // false
console.log(isNumeric(null));           // false

只允许使用正数整数,请使用以下内容:

function isNumeric(value) {
    return /^\d+$/.test(value);
}

console.log(isNumeric('123'));          // true
console.log(isNumeric('-23'));          // false

15
console.log(isNumeric('-1')); - yongnan
8
console.log(isNumeric('2e2')); - Gaël Barbin
34
也许只需将“isNumeric”重命名为“hasOnlyDigits”。在许多情况下,这正是您要查找的检查方式。 - gus3001
2
这就是我一直在寻找的,相当于 PHP ctype_digit 的功能。 - pmiguelpinto
8
稍微改进一下..禁止阿拉伯语等语言的数字字符 /^[0-9]+$/.test(value) - Devin Rhode
显示剩余9条评论

85

这个问题的被接受答案有一些缺陷(正如其他几位用户所强调的那样)。以下是在JavaScript中处理这个问题的最简单和经过验证的方法之一:

function isNumeric(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

以下是一些好的测试用例:

console.log(isNumeric(12345678912345678912)); // true
console.log(isNumeric('2 '));                 // true
console.log(isNumeric('-32.2 '));             // true
console.log(isNumeric(-32.2));                // true
console.log(isNumeric(undefined));            // false

// the accepted answer fails at these tests:
console.log(isNumeric(''));                   // false
console.log(isNumeric(null));                 // false
console.log(isNumeric([]));                   // false

8
parseFloat不适用于此应用程序,因为当它遇到第一个无法解析为数字的字符时,它将返回已解析的有效数字。例如,parseFloat('1.1ea10') === 1.1。因此需要其他方法来确保字符串被完全解析为数字。 - Ben Aston
2
注意,如果您使用Number.isNan和Number.isFinite,这将不起作用。 - Yohan Dahmani
对于字符串,Number.isNaNNumber.isFinite 不起作用,因为它们不会将字符串转换为数字。 - Cas
10,1怎么样? - Rossof Rostislav
这将无法使用数组输入 isNumeric([2])。数组类型不是数字。 - dazzafact
!isNaN(parseFloat('0111')) && isFinite('0111') === true - MrYellow

77

你可以使用正则表达式的方式:

var num = "987238";

if(num.match(/^-?\d+$/)){
  //valid integer (positive or negative)
}else if(num.match(/^\d+\.\d+$/)){
  //valid float
}else{
  //not valid number
}

54
在这种情况下,RegExp == 不好。 - Joel Coehoorn
11
这在十六进制数(例如0x12),没有前导零的浮点数(如.42)和负数上会失败。 - Ori
23
能否详细解释一下为什么在这里使用 RegExp 是不好的?在我看来,这似乎是一个有效的用例。 - computrius
7
建立一个数字的方法比看起来的要多(另一个评论中的十六进制数字只是一个例子),还有很多数字可能不被认为是有效的(类型溢出、过于精确等)。此外,正则表达式比仅使用内置机制更慢且更复杂。 - Joel Coehoorn
2
应该也能匹配科学计数法,如1e10等。 - Joseph Merdrignac
显示剩余4条评论

54
如果您确实要确保一个字符串只包含数字(整数或浮点数),并且恰好是一个数字,则不能仅使用parseInt()/ parseFloat(),Number()或!isNaN()。请注意,当Number()返回数字时,!isNaN()实际上返回true,并且返回NaN时返回false,因此我将在本文中排除它。 parseFloat()的问题在于,如果字符串包含任何数字,则它将返回数字,即使该字符串不仅包含并且恰好包含一个数字:
parseFloat("2016-12-31")  // returns 2016
parseFloat("1-1") // return 1
parseFloat("1.2.3") // returns 1.2
Number()存在的问题是,即使传递的值根本不是数字,它仍会返回一个数字!
Number("") // returns 0
Number(" ") // returns 0
Number(" \u00A0   \t\n\r") // returns 0

使用自己编写的正则表达式的问题在于,除非你能够创建与Javascript识别浮点数完全匹配的正则表达式,否则你将会错过一些情况或者认为不应该被识别的情况被认为是正确的。而且,即使你可以编写自己的正则表达式,为什么要这样做呢?有更简单的内置方法可以完成这个任务。
然而,事实证明,在parseFloat()返回数字的每种情况下,Number()(和isNaN())都会做正确的事情,并反之亦然。因此,为了确定一个字符串是否确切且只是一个数字,请调用两个函数,并查看它们是否同时返回true:
function isNumber(str) {
  if (typeof str != "string") return false // we only process strings!
  // could also coerce to string: str = ""+str
  return !isNaN(str) && !isNaN(parseFloat(str))
}

2
当字符串具有前导或尾随空格时,此函数返回true。 ' 1''2 '' 3'都会返回true。 - Rudey
将类似以下内容添加到return语句中即可解决问题:&&!/ ^ \ s + | \ s + $ / g.test(str) - Ultroman the Tacoman
2
@RuudLenders - 大多数人不会在去掉尾随空格以使字符串成为有效数字时关心,因为在许多界面中意外添加额外的空格很容易。 - Ian
9
如果数字字符串来自用户输入,那么这是正确的。但我认为我应该提到空格,因为我认为大多数需要“isNumber”函数的人并不涉及用户界面。此外,一个好的数字输入不会允许以空格开头。 - Rudey
1
@Michael 谢谢您发布这篇文章。对于我这个刚开始学习 JavaScript 的新手来说,这篇文章真的帮了我很多。在学术领域中,虽然它不是一个实际问题,但字符串"Infinity"被上述函数解析为数字(true),这是有道理的,因为在 JavaScript 中 Infinity 是一个数字 :-)。 Numeric() 和 parseFloat 都可以将字符串转换为 Infinity。感谢您的分享,祝好。 - Alexander Hunter

36

2019: 包括ES3、ES6和TypeScript示例

也许这个问题已经被反复讨论过很多次了,但是今天我也遇到了这个问题,并且想要发布我的答案,因为我没有看到其他任何一个答案能够像我这样简单而彻底地解决它:

ES3

var isNumeric = function(num){
    return (typeof(num) === 'number' || typeof(num) === "string" && num.trim() !== '') && !isNaN(num);  
}

ES6

const isNumeric = (num) => (typeof(num) === 'number' || typeof(num) === "string" && num.trim() !== '') && !isNaN(num);

类型脚本

const isNumeric = (num: any) => (typeof(num) === 'number' || typeof(num) === "string" && num.trim() !== '') && !isNaN(num as number);

这看起来非常简单,涵盖了我在许多其他帖子和自己想到的所有基础知识:

// Positive Cases
console.log(0, isNumeric(0) === true);
console.log(1, isNumeric(1) === true);
console.log(1234567890, isNumeric(1234567890) === true);
console.log('1234567890', isNumeric('1234567890') === true);
console.log('0', isNumeric('0') === true);
console.log('1', isNumeric('1') === true);
console.log('1.1', isNumeric('1.1') === true);
console.log('-1', isNumeric('-1') === true);
console.log('-1.2354', isNumeric('-1.2354') === true);
console.log('-1234567890', isNumeric('-1234567890') === true);
console.log(-1, isNumeric(-1) === true);
console.log(-32.1, isNumeric(-32.1) === true);
console.log('0x1', isNumeric('0x1') === true);  // Valid number in hex
// Negative Cases
console.log(true, isNumeric(true) === false);
console.log(false, isNumeric(false) === false);
console.log('1..1', isNumeric('1..1') === false);
console.log('1,1', isNumeric('1,1') === false);
console.log('-32.1.12', isNumeric('-32.1.12') === false);
console.log('[blank]', isNumeric('') === false);
console.log('[spaces]', isNumeric('   ') === false);
console.log('null', isNumeric(null) === false);
console.log('undefined', isNumeric(undefined) === false);
console.log([], isNumeric([]) === false);
console.log('NaN', isNumeric(NaN) === false);

您也可以尝试自己编写isNumeric函数,然后将这些用例粘贴进去,并扫描所有用例的返回值是否为“true”。

或者,查看每个用例的返回值:

<code>isNumeric()</code>针对每个测试的结果


1
很好,除了例如'0x10'(返回true!) - S.Serpooshan
1
@S.Serpooshan,0x10应该返回true,因为它是一个十六进制数。测试用例中显示了0x1,并且预计返回true,因为它是一个数字。如果您的特定用例要求将十六进制数视为字符串,则需要稍微不同地编写解决方案。 - Jeremy
是的,这取决于我们的情况。 - S.Serpooshan
2
也适用于科学计数法:isNumeric('3e2') / isNumeric(3e2) - Kieran101
3
TypeScript版本的类型应该是unknown: const isNumeric = (num: unknown) - Milan
(typeof('0111') === 'number' || typeof('0111') === "string" && '0111'.trim() !== '') && !isNaN('0111'); 因此数据丢失,即意图的零前缀。 - MrYellow

31

尝试使用isNaN函数

isNaN()函数用于确定一个值是否为不合法的数字(非数值)。

如果值等于NaN,则该函数返回true。否则,返回false。

此函数与Number特定的Number.isNaN()方法不同。

  全局的isNaN()函数会将被测试的值转换为数字,然后进行测试。

Number.isNan()不会将值转换为数字,并且对任何不属于Number类型的值都不会返回true...


4
确保添加对空字符串的检查。isNaN('')返回false,但在这种情况下,您可能希望它返回true。 - Michael Haren
3
isFinite是更好的检查方式 - 它处理了无穷大这种奇怪的边界情况。 - JonnyRaa
4
不够好!isNaN()对于任何仅包含空格字符的字符串,包括像'\u00A0'这样的字符,都会返回false。 - Michael
5
警告:不适用于值为 null、""(空字符串)和 false。 - Krisztián Balla
我意识到这个答案是11年前给出的,比被接受的答案早几分钟,但不管你喜不喜欢,被接受的答案有更多的讨论,所以这个答案并没有真正为回答问题做出任何贡献。我建议您删除它,以避免分散新读者的注意力。我还认为如果您这样做,您将获得“自律”徽章。 - Dan Dascalescu

30

TL;DR

这取决于你想将什么解析为数字。

内置函数比较

由于现有的资料都不能满足我的需求,我试图搞清楚这些函数实际上是如何工作的。

对于这个问题的三个直接回答感觉如下:

  1. !isNaN(input)(它与 +input === +input 的输出相同)
  2. !isNaN(parseFloat(input))
  3. isFinite(input)

但它们在每种情况下都正确吗?

我在几种情况下测试了这些功能,并生成了 markdown 输出,看起来像这样:

input !isNaN(input) 或者
+input===+input
!isNaN(
parseFloat(
input))
isFinite(
input)
评论
123 ✔️ ✔️ ✔️ -
'123' ✔️ ✔️ ✔️ -
12.3 ✔️ ✔️ ✔️ -
'12.3' ✔️ ✔️ ✔️ -
'   12.3   ' ✔️ ✔️ ✔️ 剪裁空白字符,如预期所示。
1_000_000 ✔️ ✔️ ✔️ 理解数字分隔符,也是预期的。
'1_000_000' ✔️ 惊喜!JS在字符串中不会解析数字分隔符。有关详情,请查看this问题。(那么为什么将其解析为浮点数呢?好吧,它没有。)
'0b11111111' ✔️ ✔️ ✔️ 理解二进制形式,应该是这样的。
'0o377' ✔️ ✔️ ✔️ 八进制形式也被理解了。
'0xFF' ✔️ ✔️ ✔️ 当然,十六进制是可以理解的。有人认为会有什么问题吗?
''那么,哪一个是“正确”的呢?

现在应该清楚了,这主要取决于我们的需求。例如,我们可能希望将空输入视为0。在这种情况下,isFinite() 就可以正常工作。

同样地,当需要将1010000000000 视为有效数字时,也许我们可以从 isNaN() 中获得一些帮助(尽管问题仍然存在——为什么它会是有效数字,我们如何处理它)!

当然,我们可以手动排除任何一种情况。

就像在我的情况下,我需要完全符合 isFinite() 的输出,除了空值、空字符串和仅包含空格的字符串的情况。而且我不用担心 非常大 的数字。因此,我的代码看起来像这样:

/**
 * My necessity was met by the following code.
 */

if (input === null) {
    // Null input
} else if (input.trim() === '') {
    // Empty or whitespace-only string
} else if (isFinite(input)) {
    // Input is a number
} else {
    // Not a number
}

而且,这是我生成表格的 JavaScript 代码:

/**
 * Note: JavaScript does not print numeric separator inside a number.
 * In that single case, the markdown output was manually corrected.
 * Also, the comments were manually added later, of course.
 */

let inputs = [
    123, '123', 12.3, '12.3', '   12.3   ',
    1_000_000, '1_000_000',
    '0b11111111', '0o377', '0xFF',
    '', '    ',
    'abc', '12.34Ab!@#$',
    '10e100', '10e1000',
    null, undefined, Infinity];

let markdownOutput = `| \`input\` | \`!isNaN(input)\` or <br>\`+input === +input\` | \`!isNaN(parseFloat(input))\` | \`isFinite(input)\` | Comment |
| :---: | :---: | :---: | :---: | :--- |\n`;

for (let input of inputs) {
    let outputs = [];
    outputs.push(!isNaN(input));
    outputs.push(!isNaN(parseFloat(input)));
    outputs.push(isFinite(input));

    if (typeof input === 'string') {
        // Output with quotations
        console.log(`'${input}'`);
        markdownOutput += `| '${input}'`;
    } else {
        // Output without quotes
        console.log(input);
        markdownOutput += `| ${input}`;
    }

    for (let output of outputs) {
        console.log('\t' + output);
        if (output === true) {
            markdownOutput += ` | <div style="color:limegreen">true</div>`;
            // markdownOutput += ` | ✔️`; // for stackoverflow
        } else {
            markdownOutput += ` | <div style="color:orangered">false</div>`;
            // markdownOutput += ` | ❌`; // for stackoverflow
        }
    }

    markdownOutput += ` ||\n`;
}

// Replace two or more whitespaces with $nbsp;
markdownOutput = markdownOutput.replaceAll(`  `, `&nbsp;&nbsp;`);

// Print markdown to console
console.log(markdownOutput);

1
而且还有一个更严谨的解决方案 - ooo
出色的回答。赞! - Brad
“'0123'”是一个字符串,预期它仍然是一个字符串,但任何这些技术都会检测到一个数字,并导致“0”丢失。 - MrYellow

25
JavaScript全局函数isFinite()用于检查一个值是否是有效(有限)数字。
请参阅MDN以了解Number.isFinite()和global isFinite()之间的区别

let a = isFinite('abc') // false
let b = isFinite('123') // true
let c = isFinite('12a') // false
let d = isFinite(null)  // true
console.log(a, b, c, d)


6
isFinite(null) 返回 true! - Harry
1
@Harry,根据Mozilla文档,使用Number.isFinite(null)会更加健壮,并返回false。缺点是如果您想接受'0',这也会返回false。如果想要使用这个方法,最好先拒绝null,然后再使用isFinite。 - danefondo
1
还要注意 isFinite([]) 和 isFinite('') 的返回值为 true。 - Alice Chan

18

虽然这是一个旧问题,但给出的答案中还有一些要点缺失。

科学计数法。

!isNaN('1e+30') 的值为 true,但在大多数情况下,当人们要求数字时,他们不希望匹配类似于 1e+30 的内容。

大浮点数可能会表现出奇怪的行为

观察以下示例(使用Node.js):

> var s = Array(16 + 1).join('9')
undefined
> s.length
16
> s
'9999999999999999'
> !isNaN(s)
true
> Number(s)
10000000000000000
> String(Number(s)) === s
false
>

另一方面:

> var s = Array(16 + 1).join('1')
undefined
> String(Number(s)) === s
true
> var s = Array(15 + 1).join('9')
undefined
> String(Number(s)) === s
true
>

因此,如果有人期望 String(Number(s)) === s,那么最好将字符串限制在最多15个数字(忽略前导零)。

无穷大

> typeof Infinity
'number'
> !isNaN('Infinity')
true
> isFinite('Infinity')
false
>

鉴于这一切,检查给定的字符串是否是满足以下所有条件的数字并不是一件容易的事情:

  • 非科学计数法
  • 可以可靠地转换为 Number,然后再转回 String
  • 有限的

下面是一个简单版本:

  function isNonScientificNumberString(o) {
    if (!o || typeof o !== 'string') {
      // Should not be given anything but strings.
      return false;
    }
    return o.length <= 15 && o.indexOf('e+') < 0 && o.indexOf('E+') < 0 && !isNaN(o) && isFinite(o);
  }

然而,即使这个方法也远未完美。该方法未处理前导零,但前导零会影响长度测试。


2
然而,在大多数情况下,当人们要求数字时,他们并不想匹配像1e+30这样的东西。为什么会这样说呢?如果有人想知道一个字符串是否包含数字,那么我认为他们想知道它是否包含数字,而1e+30是一个数字。当然,如果我在JavaScript中测试一个字符串的数值,我希望它能匹配上。 - Dan Jones

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