使用JavaScript将字符串转换为标题大小写

779

有没有一种简单的方法将字符串转换为标题格式?例如,john smith 变成 John Smith。我不想要像John Resig的解决方案那样复杂的东西,只是(希望)一种一两行代码就能实现的方法。


1
有许多方法,我们有一些性能统计数据吗? - theAnubhav
1
@theAnubhav 是的,我们现在有一个基准 - Ulysse BN
2
到了2022年,浏览器仍然没有本地功能来执行此操作。 - Sơn Trần-Nguyễn
1
这种大小写格式完全取决于语言/地点/文化。 - James Moore
我希望解决方案的一个测试用例是“Comhrá i mBÁC le Seán Nguyen” - 祝你好运!基本上,计算机可以执行称为“标题大小写”的操作的想法可能是没有希望的,即使给定了大量的机器学习资源。 - James Moore
69个回答

963

试一下这个:

function toTitleCase(str) {
  return str.replace(
    /\w\S*/g,
    function(txt) {
      return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
    }
  );
}
<form>
  Input:
  <br /><textarea name="input" onchange="form.output.value=toTitleCase(this.value)" onkeyup="form.output.value=toTitleCase(this.value)"></textarea>
  <br />Output:
  <br /><textarea name="output" readonly onclick="select(this)"></textarea>
</form>


19
为什么在\w+\w*的情况下要使用\w\S*呢?我不明白为什么你想包括空格以外的内容,这会使 Jim-Bob 变成 Jim-bob - martinczerwi
5
@martinCzerwi 中的\w\S*也导致了我们这边的Jim-bob问题。使用\w*解决了这个问题。 - Bouke
15
/([^\W_]+[^\s-]*) */g 可以解决 Jim-Bob 问题,即:*jim-bob* --> *Jim-Bob*。 - recursion.ninja
27
如果你希望把 *jim-bob --> Jim-Bob*,你应该使用 */\b\w+/g*。例如:str.replace(/\b\w+/g,function(s){return s.charAt(0).toUpperCase() + s.substr(1).toLowerCase();}); - vol7ron
8
为了避免像“Don't”被修改为“Don'T”的情况发生,使用\w\S*而不是\w+\w*,但正如其他人指出的,\w\S*会对连字符连接的单词造成问题。 - doubleDown
显示剩余19条评论

319
如果CSS解决方案符合您的需求,您可以将text-transform CSS样式应用于您的控件:
text-transform: capitalize;

请注意,这将转换以下内容:
hello worldHello World
HELLO WORLD 不会改变
emily-jane o'brienEmily-jane O'brien(不正确)
Maria von TrappMaria Von Trapp(不正确)


67
这段CSS代码有效,但与大多数人的期望不同,因为如果文本一开始就是全大写的,它就没有效果。http://www.webmasterworld.com/forum83/7506.htm - whitneyland
72
JS用于浏览器之外的场合。 - mikemaccana
13
有一个简单的方法可以将字符串转换为标题格式吗?这个答案并不会转换字符串,它只是将一种样式应用到了字符串上。 - kingPuppy
21
这个问题比较模糊,没有解释使用场景。也许他们想用JS转换字符串,但实际上CSS对他们更好,就像我的情况一样。 - Christian Sirolli
2
把“JavaScript”放进垃圾桶吧,它就在问题里面。 - Iest
显示剩余9条评论

235

稍微更优雅的方式,改编自Greg Dean的函数:

String.prototype.toProperCase = function () {
    return this.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
};

使用以下方式调用:

"pascal".toProperCase();

5
请记住,如果您的用户名中带有破折号并输入“Jo-Ann Smith”,此代码将将其转换为“Jo-ann Smith”(请注意“Ann”中的小写字母“a”)。 - dbau
20
为什么他不应该改变String原型?我认为这是一个好的解决方案。想想Ruby的开放类,向现有类添加函数是完全有效且被广泛接受的。 - marco-fiset
118
因为它与其他库不兼容!当两个库都试图使用不兼容的更改修改原生JavaScript对象时,会发生糟糕的事情。想象一下如果jQuery和Google Maps都遵循这种设计模式,你无法在同一页上同时使用它们。 - SavoryBytes
6
很好的观点。加上方法名称前缀应该有助于避免这种情况,同时使方法不可枚举也会有帮助。 - mikemaccana
71
我觉得“不要修改原生JavaScript对象”这一说法就像“永远不要使用goto”或“eval是邪恶的”一样。在许多情况下,这是可以接受的。如果您完全控制您的项目并且当然不打算将其发布为库,我认为这种方法没有问题。 - FoxMulder900
显示剩余4条评论

184

这是我的版本,我认为它易于理解并且也很优雅。

const str = "foo bar baz";
const newStr = str.split(' ')
   .map(w => w[0].toUpperCase() + w.substring(1).toLowerCase())
   .join(' ');
console.log(newStr);


3
另外一种方法是在映射中将子字符串转换为小写: str.split(' ').map(i => i[0].toUpperCase() + i.substring(1).toLowerCase()).join(' ') - Dave Land
10
我不同意使用.toLowerCase()方法。像“McDonald”这样的名称或缩写词“ASAP”应该保留它们的大写字母。如果有人实际上传入了类似于“heLLO”的字符串,应用程序不应假定大写字母是错误的。 - Thomas Higginbotham
1
@ThomasHigginbotham 这个怎么样?String.prototype.toTitleCase = function (blnForceLower) { var strReturn; (blnForceLower ? strReturn = this.toLowerCase() : strReturn = this); return strReturn .split(' ') .map(i => i[0].toUpperCase() + i.substr(1)) .join(' '); } - Sean Kendle
3
如果“str”是单个字符,这将会出现错误。 - Madbreaks
2
这看起来像是最快的解决方案 :) - Ulysse BN
显示剩余5条评论

121

这是我的函数,它可以将文本转换为标题格式,同时将定义的缩略词保留为大写字母,次要单词保留为小写字母:

String.prototype.toTitleCase = function() {
  var i, j, str, lowers, uppers;
  str = this.replace(/([^\W_]+[^\s-]*) */g, function(txt) {
    return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
  });

  // Certain minor words should be left lowercase unless 
  // they are the first or last words in the string
  lowers = ['A', 'An', 'The', 'And', 'But', 'Or', 'For', 'Nor', 'As', 'At', 
  'By', 'For', 'From', 'In', 'Into', 'Near', 'Of', 'On', 'Onto', 'To', 'With'];
  for (i = 0, j = lowers.length; i < j; i++)
    str = str.replace(new RegExp('\\s' + lowers[i] + '\\s', 'g'), 
      function(txt) {
        return txt.toLowerCase();
      });

  // Certain words such as initialisms or acronyms should be left uppercase
  uppers = ['Id', 'Tv'];
  for (i = 0, j = uppers.length; i < j; i++)
    str = str.replace(new RegExp('\\b' + uppers[i] + '\\b', 'g'), 
      uppers[i].toUpperCase());

  return str;
}
例如:
"TO LOGIN TO THIS SITE and watch tv, please enter a valid id:".toTitleCase();
// Returns: "To Login to This Site and Watch TV, Please Enter a Valid ID:"

2
已修复。第三行中的正则表达式已从/\w\S*/g更改为/([^\W_]+[^\s-]*) */g,以解决@awashburn上面的评论所提出的问题。 - Geoffrey Booth
2
使用您的正则表达式模式是否比我认为更易于理解的/\b\w+/g有优势? - Michael
2
我看不出来有什么问题,我只是采纳了另一个评论者的建议来解决连字符单词的问题;但是你的正则表达式似乎同样有效,而且简单总是更好。为了后人和未来访问此帖子的人们,我刚刚根据@Michael的评论将第三行中的正则表达式从/([^\W_]+[^\s-]*) */g更改为/\b\w+/g;如果您发现需要更复杂的正则表达式,请在评论中指出。 - Geoffrey Booth
1
我将第三行的正则表达式更改为/\b[\w-\']+/g,以允许单词中包含连字符和撇号。 - Shamasis Bhattacharya
1
为了提醒我们语言的混乱,存在着弗洛伊德的本我,它不是全部用大写字母书写的。除非你在喊叫,这是一件非常本我风格的事情。 - tylertrotter
显示剩余6条评论

61
我更喜欢以下答案,它只匹配每个单词的第一个字母并将其大写。代码更简单,易于阅读且字节数更少。它保留现有的大写字母以防止缩写变形。但是你可以在字符串上调用 toLowerCase() 方法。
function title(str) {
  return str.replace(/(^|\s)\S/g, function(t) { return t.toUpperCase() });
}

你可以将以下代码添加到字符串原型中,这样你就可以使用'my string'.toTitle()进行操作:
String.prototype.toTitle = function() {
  return this.replace(/(^|\s)\S/g, function(t) { return t.toUpperCase() });
}

Example:

String.prototype.toTitle = function() {
  return this.replace(/(^|\s)\S/g, function(t) { return t.toUpperCase() });
}

console.log('all lower case ->','all lower case'.toTitle());
console.log('ALL UPPER CASE ->','ALL UPPER CASE'.toTitle());
console.log("I'm a little teapot ->","I'm a little teapot".toTitle());


6
作为lambda函数,它更加简洁优美 const titleCase = (str) => str.replace(/\b\S/g, t => t.toUpperCase()); - 0xcaff
但是“标题大小写”是有意义的。全大写的句子不会被合理地认为是标题大小写,而您的答案允许这种情况发生。https://en.wikipedia.org/wiki/Letter_case#Title_case - 我喜欢您的答案,但在这种情况下它是不完整的。 - Madbreaks
6
尽管你最初的例子有些牵强,但你指出了一个很好的观点,即如果输入字符是大写的,输出结果不会改变。话虽如此,我认为现在的答案(编辑建议使用 toLowerCase)比假设开发人员意图的答案更加灵活/有用。这种方法也反映了其他编程语言内置函数的功能,例如PHP(ucwords)和Golang(strings.Title)。有趣的是,.NET(TextInfo.ToTitleCase)适用于混合大小写,但它也会保留全大写的字符串不变。 - Tom Kay
谢谢 - 我没有注意到你添加了 toLowerCase,这对我来说已经足够好了。+1,干杯 - Madbreaks
感谢您的反馈@rcoup - 如果我将其更改为匹配(空格或行首),与边界相比,您能想到任何向后兼容性问题吗? - Tom Kay
显示剩余5条评论

58
你可以立即将字符串转换为小写,然后只需将每个单词的第一个字母转换为大写。这会变成一个非常简单的一行代码:
function titleCase(str) {
  return str.toLowerCase().replace(/\b\w/g, s => s.toUpperCase());
}

console.log(titleCase('iron man'));
console.log(titleCase('iNcrEdible hulK'));


1
@Waz,感谢你的想法!只是想澄清一下 =>,它是一个本地箭头函数(ES6),链接跳转到了Mozilla Docs上的相关内容,其中还提供了支持表。 - KevBot
2
字符串 => 字符串.toLowerCase().replace(/\b\w/g, word_head => word_head.toUpperCase()); - Константин Ван
1
正如@КонстантинВан所指出的,不使用分组,解决方案会更快。 - dovid
1
@КонстантинВан和dovid,感谢你们的反馈!我已经实现了你们的建议。我查看了一些差异的基准测试,你们是正确的,它确实更快。 - KevBot
这里需要注意的一件事是,像“rachael's tasks”这样的字符串会在第一个“s”上产生误报匹配,并且会不适当地将其大写。因此,对于某些需求,无法使用上述技术,因为它仅适用于简单情况。另外,@Ulysse BN的答案在这些情况下确实有效,供您参考。 - sean2078
显示剩余4条评论

40

基准测试

简短摘要

该基准测试的赢家是普通的for循环:

function titleize(str) {
    let upper = true
    let newStr = ""
    for (let i = 0, l = str.length; i < l; i++) {
        // Note that you can also check for all kinds of spaces  with
        // str[i].match(/\s/)
        if (str[i] == " ") {
            upper = true
            newStr += str[i]
            continue
        }
        newStr += upper ? str[i].toUpperCase() : str[i].toLowerCase()
        upper = false
    }
    return newStr
}
// NOTE: you could beat that using charcode and string builder I guess.

细节

我选取了最受欢迎和独特的答案,并制作了基准测试

这是在我的 MacBook Pro 上的结果:

enter image description here

为了完整起见,这里列出了使用的函数:

str = "the QUICK BrOWn Fox jUMPS oVeR the LAzy doG";
function regex(str) {
  return str.replace(
    /\w\S*/g,
    function(txt) {
      return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
    }
  );
}

function split(str) {
  return str.
    split(' ').
    map(w => w[0].toUpperCase() + w.substr(1).toLowerCase()).
    join(' ');
}

function complete(str) {
  var i, j, str, lowers, uppers;
  str = str.replace(/([^\W_]+[^\s-]*) */g, function(txt) {
    return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
  });

  // Certain minor words should be left lowercase unless 
  // they are the first or last words in the string
  lowers = ['A', 'An', 'The', 'And', 'But', 'Or', 'For', 'Nor', 'As', 'At', 
  'By', 'For', 'From', 'In', 'Into', 'Near', 'Of', 'On', 'Onto', 'To', 'With'];
  for (i = 0, j = lowers.length; i < j; i++)
    str = str.replace(new RegExp('\\s' + lowers[i] + '\\s', 'g'), 
      function(txt) {
        return txt.toLowerCase();
      });

  // Certain words such as initialisms or acronyms should be left uppercase
  uppers = ['Id', 'Tv'];
  for (i = 0, j = uppers.length; i < j; i++)
    str = str.replace(new RegExp('\\b' + uppers[i] + '\\b', 'g'), 
      uppers[i].toUpperCase());

  return str;
}

function firstLetterOnly(str) {
  return str.replace(/\b(\S)/g, function(t) { return t.toUpperCase(); });
}

function forLoop(str) {
  let upper = true;
  let newStr = "";
  for (let i = 0, l = str.length; i < l; i++) {
    if (str[i] == " ") {
      upper = true;
        newStr += " ";
      continue;
    }
    newStr += upper ? str[i].toUpperCase() : str[i].toLowerCase();
    upper = false;
  }
  return newStr;
}

请注意,我故意没有更改原型,因为我认为这是一种非常糟糕的做法,我不认为我们应该在回答中推广这种做法。只有在你是唯一的工作人员且代码库很小的情况下才可以这样做。
如果您想添加其他方法到这个基准测试,请在评论中留下答案链接!
2022年Mac M1编辑: 在我的新电脑上,使用更新的chrome浏览器,split获胜。如果您真的关心特定设备上的性能,您应该自己运行基准测试。

1
“首字母”方法中的括号组没有被消耗,如果将它们移除,则该方法会失效,因为这是有意义的。 - dovid
1
str.toLowerCase().replace(/\b\S/g, function(t) { return t.toUpperCase(); }); 这段代码也使用了正则表达式方法,并且实现了相同的结果。 - dovid
complete方法的显著改进:https://jsben.ch/yTK3Y - dovid
1
@dovid 我必须承认我直接在基准测试中聚合了其他答案。因此,你可以随意编辑我的答案,并建议你直接评论相关的答案 :) - Ulysse BN

26

var result =
  'this is very interesting'.replace(/\b[a-z]/g, (x) => x.toUpperCase())

console.log(result) // This Is Very Interesting


只是一个问题,() 结构用于指定对一系列选项的匹配:也就是说,(a|b) 匹配 a 或 b。那么 (.) 这个结构是做什么用的呢? - Michael Blackburn
对于任何有同样问题的人,它定义了一个替换“blob”,该“blob”用于替换部分。这些“blob”按顺序编号,第一个()放入$1中,第二个放入$2中。我发现这个网站很有用: http://javascript.info/tutorial/regular-expressions-methods - Michael Blackburn
我无法让以上内容有效运行,但是我离正则表达式专家还很远。 我正在使用 'string'.replace(/^(.)(.*)/,function(s,p1,p2){return p1.toUpperCase()+p2;})再次说明,这只能用于将字符串的第一个字母大写,但如果这是你所需要的,那我的方法是可行的。 - Michael Blackburn
1
由于某种原因,FF35似乎无法处理'$1'.toUpperCase(),似乎在赋值时大写字母还没有被执行。通过使用函数'string'.replace(/^(.){1}/,function(match) { return match.toUpperCase(); })进行了解决。 - MrYellow
大家好,答案已经确定了!我相信当我最初发布它时它是有效的。无论如何,感谢你们的参与! - simo
这在“džungla”上不起作用。 - user3840170

25

惊讶地看到没有人提到使用rest参数。这里是一个简单的一行代码,使用了ES6 Rest参数。

let str="john smith"
str=str.split(" ").map(([firstChar,...rest])=>firstChar.toUpperCase()+rest.join("").toLowerCase()).join(" ")
console.log(str)


我很欣赏这个解决方案,并决定这是我打算使用的最佳代码。我注意到它在名称中没有考虑连字符。这对我的当前使用并没有太大影响;然而,我想知道如何调整这段代码以考虑这一点。再次感谢您提供了一个可靠的解决方案! - Paul Murray
这个实现与Python函数string.capwords(s, sep=None)等效。 - Géry Ogam

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