JavaScript中是否有strcmp()函数?

144

有人能为我验证一下吗?JavaScript没有一个版本的strcmp(),所以你必须编写类似下面的代码:

 ( str1 < str2 ) ? 
            -1 : 
             ( str1 > str2 ? 1 : 0 );

1
你并不孤单 - 其他人之前已经做过了。实际上,PHP.JS项目已经为许多其他常见函数完成了这项工作。这是一个方便的资源。 - Reed Copsey
8个回答

154

那么问题来了

str1.localeCompare(str2)

localeCompare() 看起来不错,但它似乎只适用于微软,或者最多不符合标准。 - jonathan
12
你在查看哪个标准?它似乎符合ECMA-262标准第15.5.4.9节,以及Mozilla JavaScript参考文档中的内容(https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String#Methods_unrelated_to_HTML)。 - newacct
newacct 是完全正确的。这似乎是 ECMAScript 标准。在这种情况下可能是最好的解决方案。 - coderjoe
3
localeCompare()在不同的浏览器中有时表现不同。 - Varda Elentári
2
@VardaElentári:仅适用于在给定语言环境中没有词汇排序的字符。对于具有词汇排序并且浏览器不限制使用Unicode的部分的字符,结果是一致的,并且由ECMA-402和Unicode定义(http://ecma-international.org/ecma-402/3.0/index.html#sec-implementation-dependencies)。 - T.J. Crowder
显示剩余2条评论

45

正如你所指出的那样,JavaScript 并没有这个功能。

一个快速搜索得出了以下结果:

function strcmp ( str1, str2 ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Waldo Malqui Silva
    // +      input by: Steve Hilder
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +    revised by: gorthaur
    // *     example 1: strcmp( 'waldo', 'owald' );
    // *     returns 1: 1
    // *     example 2: strcmp( 'owald', 'waldo' );
    // *     returns 2: -1

    return ( ( str1 == str2 ) ? 0 : ( ( str1 > str2 ) ? 1 : -1 ) );
}

这段内容来源于http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_strcmp/

当然,如果需要的话,你可以添加localeCompare方法:

if (typeof(String.prototype.localeCompare) === 'undefined') {
    String.prototype.localeCompare = function(str, locale, options) {
        return ((this == str) ? 0 : ((this > str) ? 1 : -1));
    };
}

无需担心本地浏览器是否已经包含,可以在代码中的任何地方使用 str1.localeCompare(str2)。唯一的问题是,如果你关心这个,你需要添加对localesoptions的支持。


1
我认为这是一种很好的处理方式(使用特性检测和 polyfill),但如果微秒级的性能对于此方法的需求如此重要,那么我有点困惑,为什么使用 == 而不是 ===,因为后者避免了类型转换,因而更快一些。 - Tokimon
关于 polyfill 的说明——localeCompare 不区分大小写,因此为了使 polyfill 也不区分大小写,您可以尝试以下代码:var a = this.toUpperCase(); var b = str.toUpperCase(); return ((a == b) ? 0 : ((a > b) ? 1 : -1)); - Kip

28

localeCompare()很慢,所以如果您不关心非英语字符字符串的“正确”排序,请尝试您原来的方法或更简洁的:

str1 < str2 ? -1 : +(str1 > str2)

在我的机器上,这比localeCompare()快了一个数量级。

+确保答案始终是数字而不是布尔值。


两个 Bug:当 str1 == str2 时没有返回 0,当 str1 > str2 时没有返回 1。 - stackunderflow
2
@stackunderflow 我在排序函数中成功地使用它。你遇到了什么错误? - 1''
1
这将返回-1、false或true,而不是-1、0或1。要使其始终返回数字,请像这样进行微调: str1 < str2 ? -1 : +(str1 > str2) - jfren484
2
还有一件事(我正在编写的代码中使用它,所以我一直在完善它):请注意这是一个区分大小写的比较('Foo'将排在'bar'之前,但'Bar'将排在'foo'之后)。这对应于OP关于strcmp的问题,但许多人可能会在这里寻找一个不区分大小写的比较。 - jfren484
10
这里有一个更加简洁的表达式:(str1 > str2) - (str1 < str2)。这个表达式的意思不变,但它看起来更整洁易读。 - Jonathan Gilbert
显示剩余3条评论

9
var strcmp = new Intl.Collator(undefined, {numeric:true, sensitivity:'base'}).compare;

用法: strcmp(string1, string2)

结果: 1 表示string1比string2大,0 表示相等,-1 表示string2比string1大。

这比 String.prototype.localeCompare 更高效。

另外,numeric:true 使它执行逻辑数字比较。


这里有一些 jsperf 结果 https://jsperf.com/localecompare-vs-intl-collator https://jsperf.com/localecompare-vs-intl-collator - Colin D
匿名的这个与strcmp相反。 - user877329
@匿名 这与strcmp相反 - undefined

1

来自JavaScript中如何检查两个字符串是否相等文章:

  1. 通常,如果您的字符串仅包含ASCII字符,则使用 === 运算符来检查它们是否相等。
  2. 但是当您的字符串包含包括组合字符(例如e + ◌́ = é)的字符时,您需要在比较相等之前先对其进行规范化,如下所示- s1.normalize() === s2.normalize()

0
在我的测试中,这比在同一组随机选择的单词上使用一对三元语句快约10%。
function strcmp( a, b ) {
    for( let i=0 ; i<Math.min( a.length, b.length ) ; i++ ) {
        const n = a.charCodeAt(i) - b.charCodeAt(i);
        if( n ) return  n && ( ( n>>31 ) || 1 );
    }
    const n = a.length - b.length;
    return  n && ( ( n>>31 ) || 1 );
}

0

所以我掉进了这个兔子洞,写了一些测试来建立直觉,结果很奇怪。 简而言之,它看起来像是localeCompare执行了一个小写操作,但等式运算符没有。这导致"ff" >= "ZZ",但localeCompare返回-1,因为"ff" <= 'zz'

为了获得最佳结果,请在浏览器控制台中查看代码日志ctrl + shift + i

第二个片段隐藏了手动测试,因此您可以看到一些随机的测试。

希望这能帮助到某人。

function stringBench(n, bench, min = 10, len = 10, logDif = false) {
  function makeid(length) {
    var result = '';
    var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    var charactersLength = characters.length;
    for (var i = 0; i < length; i++) {
      result += characters.charAt(Math.floor(Math.random() *
        charactersLength));
    }
    return result;

  }

  var a = [];
  var b = [];
  var pool = {};
  let rle = [];
  let rlc = [];

  let now;
  for (let i = 0; i < n; i++) {
    pool[i] = (makeid(min + Math.floor(Math.random() *
      len))); //10-20ish

  }
  for (let i = 0; i < bench; i++) {
    a[i] = (Math.floor(Math.random() * n));
    b[i] = (Math.floor(Math.random() * n));
  }

  console.log("now testin le vs lc on a pool of", n, " with this many samples ", bench);
  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rlc[i] = pool[a[i]].localeCompare(pool[b[i]]);
  }
  let lcDelta = Date.now() - now;
  console.log("Performed ", bench, "lc localeCompare in ", lcDelta);


  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rle[i] = pool[a[i]] <= pool[b[i]];
  }
  let leDelta = Date.now() - now;
  console.log("Performed ", bench, "le (<=) compares in ", leDelta)

  for (let i = 0; i < n; i++) {
    pool[i] = (makeid(min + Math.floor(Math.random() *
      len))); //10-20ish

  }
  for (let i = 0; i < bench; i++) {
    a[i] = (Math.floor(Math.random() * n));
    b[i] = (Math.floor(Math.random() * n));
  }


  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rle[i] = pool[a[i]] <= pool[b[i]];
  }
  let leDelta2 = Date.now() - now;
  console.log("Performed ", bench, "le (<=) compares in ", leDelta2)

  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rlc[i] = pool[a[i]].localeCompare(pool[b[i]]);
  }
  let lcDelta2 = Date.now() - now;
  console.log("Performed ", bench, "lc localeCompare in ", lcDelta2);

  function testCmp(a, b, log = true) {
    let le = a <= b;
    let ge = a >= b;
    let lc = a.localeCompare(b);
    let l = a < b;
    let g = a > b;
    if (le && ge) console.assert(lc == 0, 'le && ge -> == -> lc == 0,')
    if (le) console.assert(lc <= 0, 'le-> lc <= 0')
    if (ge) console.assert(lc >= 0, 'ge-> lc >= 0')
    if (l) console.assert(lc < 0, 'l=>lc < 0')
    if (g) console.assert(lc > 0, 'g-> lc > 0')
    if (!log) return;
    console.log(`Compare:  ${a} le ${b} `, a <= b);
    console.log(`Compare:  ${a} ge ${b}`, a >= b);
    console.log(`Compare: ${a} lc ${b}`, a.localeCompare(b));
  }

  let c = 0
  for (let i = 0; i < bench; i++) {
    if (rle[i] != rlc[i] <= 0) {
      c++;
      testCmp(pool[a[i]], pool[b[i]], true);
      console.warn(pool[a[i]], ' le != lc <= 0 ', pool[b[i]]);

    }


    // rlc[i] = pool[a[i]].localeCompare(pool[b[i]]);
  }
  console.warn(' le != lc  out of bench, num diffs: ', c);


  testCmp('ff', 'ff')
  testCmp('ff', 'fa')
  testCmp('ff', 'fz')
  testCmp('ff', 'fff')
  testCmp('ff', 'ffa')
  testCmp('ff', 'ffz')
  testCmp('ff', 'a')
  testCmp('ff', 'z')
  testCmp('ff', 'f')
  testCmp('ff', 'zff')
  testCmp('ff', 'aff')
  testCmp('ff', 'ZZ')
  testCmp('ff', 'AA')
  testCmp('FF', 'ZZ')
  testCmp('FF', 'ff')
  testCmp('FF', 'AA')
  testCmp('ff', 'ZZZ')

  console.log("Dif le - lc = ", leDelta2 - lcDelta2);

  console.log("avg le ms/Mops = ", (leDelta + leDelta2) / (bench / 1000000));
  console.log("avg lc ms/Mops = ", (lcDelta + lcDelta2) / (bench / 1000000));


  console.log("Dif  - lc = ", leDelta2 - lcDelta2);

};
stringBench(1000, 5000, 1, 3, true);
// stringBench(1000000, 1000000);//nothing is equire
// stringBench(1000, 100000000);
// stringBench(1000000, 100000000, 3, 5);
// stringBench(1000000, 100000000, 15, 20);

function stringBench(n, bench, min = 10, len = 10, logDif = false) {
  function makeid(length) {
    var result = '';
    var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    var charactersLength = characters.length;
    for (var i = 0; i < length; i++) {
      result += characters.charAt(Math.floor(Math.random() *
        charactersLength));
    }
    return result;


  }

  var a = [];
  var b = [];
  var pool = {};
  let rle = [];
  let rlc = [];

  let now;
  for (let i = 0; i < n; i++) {
    pool[i] = (makeid(min + Math.floor(Math.random() *
      len))); //10-20ish

  }
  for (let i = 0; i < bench; i++) {
    a[i] = (Math.floor(Math.random() * n));
    b[i] = (Math.floor(Math.random() * n));
  }

  console.log("now testin le vs lc on a pool of", n, " with this many samples ", bench);
  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rlc[i] = pool[a[i]].localeCompare(pool[b[i]]);
  }
  let lcDelta = Date.now() - now;
  console.log("Performed ", bench, "lc localeCompare in ", lcDelta);


  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rle[i] = pool[a[i]] <= pool[b[i]];
  }
  let leDelta = Date.now() - now;
  console.log("Performed ", bench, "le (<=) compares in ", leDelta)

  for (let i = 0; i < n; i++) {
    pool[i] = (makeid(min + Math.floor(Math.random() *
      len))); //10-20ish

  }
  for (let i = 0; i < bench; i++) {
    a[i] = (Math.floor(Math.random() * n));
    b[i] = (Math.floor(Math.random() * n));
  }


  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rle[i] = pool[a[i]] <= pool[b[i]];
  }
  let leDelta2 = Date.now() - now;
  console.log("Performed ", bench, "le (<=) compares in ", leDelta2)

  now = Date.now();
  for (let i = 0; i < bench; i++) {
    rlc[i] = pool[a[i]].localeCompare(pool[b[i]]);
  }
  let lcDelta2 = Date.now() - now;
  console.log("Performed ", bench, "lc localeCompare in ", lcDelta2);

  function testCmp(a, b, log = true) {
    let le = a <= b;
    let ge = a >= b;
    let lc = a.localeCompare(b);
    let l = a < b;
    let g = a > b;
    if (le && ge) console.assert(lc == 0, 'le && ge -> == -> lc == 0,')
    if (le) console.assert(lc <= 0, 'le-> lc <= 0')
    if (ge) console.assert(lc >= 0, 'ge-> lc >= 0')
    if (l) console.assert(lc < 0, 'l=>lc < 0')
    if (g) console.assert(lc > 0, 'g-> lc > 0')
    if (!log) return;
    console.log(`Compare:  ${a} le ${b} `, a <= b);
    console.log(`Compare:  ${a} ge ${b}`, a >= b);
    console.log(`Compare: ${a} lc ${b}`, a.localeCompare(b));
  }

  let c = 0
  for (let i = 0; i < bench; i++) {
    if (rle[i] != rlc[i] <= 0) {
      c++;
      testCmp(pool[a[i]], pool[b[i]], true);
      console.warn(pool[a[i]], ' le != lc <= 0 ', pool[b[i]]);

    }


    // rlc[i] = pool[a[i]].localeCompare(pool[b[i]]);
  }
  console.warn(' le != lc  out of bench, num diffs: ', c);



  testCmp('ff', 'fa')
  testCmp('ff', 'fz')
  testCmp('ff', 'ZZ')



  console.log("Dif le - lc = ", leDelta2 - lcDelta2);

  console.log("avg le ms/Mops = ", (leDelta + leDelta2) / (bench / 1000000));
  console.log("avg lc ms/Mops = ", (lcDelta + lcDelta2) / (bench / 1000000));







  console.log("Dif  - lc = ", leDelta2 - lcDelta2);


  // for (let i = 0; i < bench; i++) {
  //     rlc[i] != rle[i]
  //     pool[a[i]].localeCompare(pool[b[i]]);
  // }
  //
  // console.log(makeid(5));
};
stringBench(1000, 5000, 1, 3, true);
// stringBench(1000000, 1000000);//nothing is equire
// stringBench(1000, 100000000);
// stringBench(1000000, 100000000, 3, 5);
// stringBench(1000000, 100000000, 15, 20);


-1
怎么样?
String.prototype.strcmp = function(s) {
    if (this < s) return -1;
    if (this > s) return 1;
    return 0;
}

然后,将s1与2进行比较:
s1.strcmp(s2)

1
如果您能说明他们为什么不应该做他们所做的事情,那将会很有帮助。如果他们正在更改现有函数方法的工作方式,我可以理解,但在这种情况下,他们正在添加一个新的函数方法。 - Relaxing In Cyprus
3
像这样无条件地扩展原型通常是一个大忌。 - Christopher

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