在Javascript中计算推文字符数的最佳方法

17
根据 Twitter API 文档(http://dev.twitter.com/pages/counting_characters),140 字限制实际上计算的是字符串的字节数,而不是字符数。
我该如何使用 JavaScript 计算字符串的字节数?或者因为我将页面编码设置为 UTF-8,所以我的每个字符始终使用 2 个字节?
也许已经有一个好用的计数器函数可以供我使用了?

2
链接文档中哪里说了这个?我只看到的是“所有 Twitter 属性都可以通过 API 接受 UTF-8 编码的文本。” - Tomalak
4
你一定需要使用jQuery来实现这个。 - Gordon
“140个字符的推文限制实际上不是按照字符计算,而是按照字符串的字节数计算。”——你从哪里听说的呀?这个页面(https://web.archive.org/web/20110612095727/http://dev.twitter.com/pages/counting_characters)非常明确地说明:“推文长度由文本NFC规范化版本中的代码点数来衡量”。代码点数,不是字节。 - Andrea
5个回答

21

由于t.co网址缩短器的原因,仅仅计算字符数已经不再适用。查看下面这两个Twitter参考链接,了解如何处理缩短链接:

https://support.twitter.com/articles/78124-how-to-shorten-links-urls

https://dev.twitter.com/docs/tco-url-wrapper/how-twitter-wrap-urls

如果你正在寻找客户端方面的帮助,你需要结交 twitter-text.js 这位新朋友。

https://github.com/twitter/twitter-text-js

我还发布了一个函数的详细说明,该函数用于计算推文中剩余字符的数量。

http://blog.pay4tweet.com/2012/04/27/twitter-lifts-140-character-limit/

该函数如下所示:

function charactersleft(tweet) {
    var url, i, lenUrlArr;
    var virtualTweet = tweet;
    var filler = "01234567890123456789";
    var extractedUrls = twttr.txt.extractUrlsWithIndices(tweet);
    var remaining = 140;
    lenUrlArr = extractedUrls.length;
    if ( lenUrlArr > 0 ) {
        for (var i = 0; i < lenUrlArr; i++) {
            url = extractedUrls[i].url;
            virtualTweet = virtualTweet.replace(url,filler);
        }
    }
    remaining = remaining - virtualTweet.length;
    return remaining;
}

该函数返回剩余字符数,假设所有URL(包括缩短至少于20个字符的URL)都被 "t.co" 缩短为19个字符加上一个空格。

它假定已经包含了"twitter-text.js"。


由于 t.co 的字符数量在今年早些时候已经增加,您能否更新它以包含 t.co 链接的增加长度? - Paul Pettengill
2
使用最新的twitter-text.js,您可以重构该函数为:charactersleft = function(tweet) { remaining = 140 - twttr.txt.getTweetLength(tweet); return remaining; } - yigitbacakoglu

19

感谢 moluv00 的回答,它让我省去了一些搜索并使我走上了正确的轨道。 我只想分享我处理应用程序中 Twitter 字符计数(由于 Tiny URLs)的方法。

拉取请求已于 2012-05-31 合并到 GitHub 存储库 中,引入了 twttr.txt.getTweetLength(text, options) 函数,该函数考虑了 t.co URL 并定义如下:

twttr.txt.getTweetLength = function(text, options) {
    if (!options) {
        options = {
            short_url_length: 22,
            short_url_length_https: 23
        };
    }
    var textLength = text.length;
    var urlsWithIndices = twttr.txt.extractUrlsWithIndices(text);

    for (var i = 0; i < urlsWithIndices.length; i++) {
        // Subtract the length of the original URL
        textLength += urlsWithIndices[i].indices[0] - urlsWithIndices[i].indices[1];

        // Add 21 characters for URL starting with https://
        // Otherwise add 20 characters
        if (urlsWithIndices[i].url.toLowerCase().match(/^https:\/\//)) {
            textLength += options.short_url_length_https;
        } else {
            textLength += options.short_url_length;
        }
    }

    return textLength;
};

所以您的函数将变为:

function charactersleft(tweet) {
    return 140 - twttr.txt.getTweetLength(tweet);
}
此外,根据t.co的最佳实践,我们应从Twitter检索 short_url_length short_url_length_https 的值,并将它们作为选项参数传递给twttr.txt.getTweetLength 函数:

在您的应用程序中每天请求GET help/configuration一次,并缓存“short_url_length”(t.co当前的最大长度值)24小时。缓存“short_url_length_https”(基于HTTPS的t.co链接的最大长度),并将其用作基于HTTPS的URL的长度。

尤其要知道,如Twitter开发者博客中所述,某些有关t.co URL长度的更改将在2013-02-20生效。


2
我已根据Twitter的新政策更新了t.co长度http://thenextweb.com/twitter/2012/12/06/twitter-take-away-two-of-my-precious-tweet-characters/。我看到GitHub上引用的代码已经这样做了。 - mahemoff

3

正如其他人所提到的那样,twitter将链接视为长度为20的字符串。在我们的小项目中,我们最终使用了以下代码片段:

function getTweetLength(input) {
  var tmp = "";
  for(var i = 0; i < 20; i++){tmp+="o"}
  return input.replace(/(http[s]?:\/\/[\S]*)/g, tmp).length;
};

如果您正在使用angular.js,这里有一个小的筛选器可以在您的angular.js应用程序中使用:
app.filter('tweetLength', function() {
  return function(input) {
    var tmp = "";
    for(var i = 0; i < 20; i++){tmp+="o"}
    return input.replace(/(http[s]?:\/\/[\S]*)/g, tmp).length;
  };
});

使用方法非常简单:

Tweet length is {{tweet|tweetLength}}

1
非常好,谢谢。小提示,但很容易修复 - Twitter现在将链接计为23个字符而不是20个:https://twitter.com/intent/tweet?text=http://www.stackoverflow.com - darklow
这个正则表达式还不够用于 Twitter 当前的 URL 识别,它会把像 foobarbaz.ba 这样的字符串视为 URL(而不需要 http(s):// 协议前缀)。TLD 是从他们 https://github.com/twitter/twitter-text/tree/master/js/src/regexp validCCTLD.js 或 validGTLD.js(或者其他一些文件)的某些查询中得出的。 - bzzWomp

2

我该如何使用Javascript计算字符串中的字节数?还是因为我将页面编码设置为UTF-8,所以我的字符串中的每个字符总是使用2个字节?

JavaScript计算的是字符而不是字节。你根本没有问题。

"嘰嘰喳喳".length == 4
"Twitter".length == 7

更新:上述内容只适用于仅包含基本多文种平面(BMP)字符的字符串。

当字符串中包含来自BMP之外的字符(如Emoji)或组合标记时,确定字符串长度就不那么简单了。以下博客文章详细讨论了此问题,强烈建议阅读:https://mathiasbynens.be/notes/javascript-unicode


1
JavaScript 计算 UTF-16 代码单元,但 Twitter 文档要求规范化后的码点。 - Andrea
你能给我一个示例字符串吗,在该字符串中 JavaScript 报告的长度与 Twitter 不同? - Tomalak
1
任何非基本多文种平面字符在JS中被计为两个字符,但在Twitter中只算一个。JavaScript会将基础字符和组合变音标记的组合报告为与单个预组合字符长度不同,但Twitter则不会。 - Andrea
啊,你说的对,谢谢!我发现这个链接:https://mathiasbynens.be/notes/javascript-unicode。不过有趣的是,其他回答里甚至都没有提到这个奇怪的点。 - Tomalak

0
Twitter将以下字符范围视为1个字符:
U+0000..U+10ff, U+2000..U+200D, U+2010..U+201F, U+2032..U+2037

其他所有内容都被计算为2个字符。

考虑到Javascript将超出U+10000的字符编码为U+D800..U+DFFF范围内的UTF-16代理对(例如"".length == 2),我想出了以下解决方案

function count_tweet_chars(str){
  str = str.normalize('NFC');
  var char2 = str.match(/[^\u0000-\u10ff,\u2000-\u200D,\u2010-\u201F,\u2032-\u2037,\uD800-\uDfff]/g) || [];
  return str.length + char2.length;
}

请注意,上述内容不包括将URL替换为t.co 23个字符的URL,并且也无法正确计算多字符字形,例如


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