正则表达式分割驼峰式字符串

95

我在JavaScript中有一个正则表达式,用以下代码(此代码来自这里)来将我的驼峰字符串按大写字母拆分:

"MyCamelCaseString"
    .replace(/([A-Z])/g, ' $1')
    .replace(/^./, function(str){ return str.toUpperCase(); })

因此返回:

"My Camel Case String"

这很不错。但是,我想再进一步。有人能帮我写一个正则表达式吗?只有在前一个字符为小写字母且后一个字符为大写字母时才进行分割。

因此,上述示例将得到我期望的结果,但如果我输入:

"ExampleID"

然后我得到返回:

"Example ID"

不是

"Example I D"

因为它会在每个大写字母处进行分割,忽略之前的任何内容。

希望这样说起来有意义!谢谢 :)


http://www.regular-expressions.info/lookaround.html - Mchl
9
(高级) -- 甜。 - devnull
我的驼峰字符串 - urzeit
1
可能是RegEx to split camelCase or TitleCase (advanced)的重复问题。 - Samuel Liew
3
尝试使用 replace(/([a-z])([A-Z])/g, '$1 $2')。 (将小写字母和大写字母之间的空格替换为一个空格) - urzeit
我从标题中删除了“(高级)”一词,以防人们不知道@devnull在说什么。它并不真正属于那里。 - Joe
15个回答

178

我猜测需要将/(A-Z)/替换为/(a-z)(A-Z)/,并将'$1'替换为'$1 $2'

"MyCamelCaseString"
    .replace(/([a-z])([A-Z])/g, '$1 $2');

/([a-z0-9])([A-Z])/ 用于将数字视为小写字符计数。

console.log("MyCamelCaseStringID".replace(/([a-z0-9])([A-Z])/g, '$1 $2'))


2
@keldar 这对数字无效。Test1Test2将保持不变。 - Broxzier
10
更一般地说:console.log('File1NotFoundTBA'.replace(/([^A-Z])([A-Z])/g, '$1 $2')); - Peter Behr
5
需要注意的是,这些示例不是驼峰式命名法(即首字母小写)。如果希望从真正的驼峰式字符串中生成类似标题的效果,则仍然需要使用以下代码将第一个字母大写: str[0].toUpperCase() + str.substring(1); - racamp101
1
虽然这个回答解决了OP的问题,但它没有处理ThisIsASlugTest1Test2。你会得到像This Is ASlugTest1Test2这样的结果,而不是This Is A SlugTest1 Test2 - kimbaudi
1
它是如何工作的呢?我们只指定了$1和$2,即使有许多驼峰式的例子,它也可以正常工作。 - Exploring
显示剩余2条评论

49
"MyCamelCaseString".replace(/([a-z](?=[A-Z]))/g, '$1 ')

输出:

"My Camel Case String"

1
'ThisIsASlug'.replace(/([a-z](?=[A-Z]))/g, '$1 ') gives "This Is ASlug" - kimbaudi
不错,但是这个字符串是PascalCase。__. - JsonKody
@JsonKody 不对,camelCase 可以是 CamelCase,但 PascalCase 不能是 pascalCase - undefined
@Legna ... 这就是为什么当你看到'ThisString'时,它明显可以被识别为PascalCase,但它不仅仅是'camelCase的首字母大写'。在这种情况下,尽管PascalCase和camelCase的定义有交集,但需要注意的是,尽管这种命名约定对于PascalCase来说是标准的,但对于camelCase来说却是非典型或较少见的情况。 - undefined

28

如果你想要一个由小写单词组成的数组:

"myCamelCaseString".split(/(?=[A-Z])/).map(s => s.toLowerCase());

如果您想要一串小写单词:

"myCamelCaseString".split(/(?=[A-Z])/).map(s => s.toLowerCase()).join(' ');

如果你想分离单词但保留大小写:

"myCamelCaseString".replace(/([a-z])([A-Z])/g, '$1 $2')

1
我之前不知道正则表达式可以在split函数内使用。在我的情况下,我在split后使用了join。非常简单。 - dezinezync
将所有内容放在一起以处理更高级的情况:'File1NotFoundTBA'.split(/(?<![A-Z])(?=[A-Z])/) 得到 [ 'File1', 'Not', 'Found', 'TBA' ] - Trevor Robinson
我希望它也能将 ThisIsASlug 分割成 This Is A Slug。不幸的是,使用这种方法得到的结果是 This Is ASlug - kimbaudi
@kimbaudi 那就直接使用上面的原始版本:'ThisIsASlug'.split(/(?=[A-Z])/) - Trevor Robinson
@TrevorRobinson,使用'ThisIsASlug'.split(/(?=[A-Z])/)可以得到["This", "Is", "A", "Slug"],但对于ExampleID并不适用。'ExampleID'.split(/(?=[A-Z])/)会返回["Example", "I", "D"]。因此我将不使用这两种方法。 - kimbaudi
显示剩余2条评论

22

有时候camelCase(驼峰式)字符串会包含缩写词,例如:

PDFSplitAndMergeSamples
PDFExtractorSDKSamples
PDFRendererSDKSamples
BarcodeReaderSDKSamples

在这种情况下,以下函数将有效。它会把字符串分割成单独的字符串,保留缩写:

function SplitCamelCaseWithAbbreviations(s){
   return s.split(/([A-Z][a-z]+)/).filter(function(e){return e});
}

示例:

function SplitCamelCaseWithAbbreviations(s){
   return s.split(/([A-Z][a-z]+)/).filter(function(e){return e});
}

console.log(SplitCamelCaseWithAbbreviations('PDFSplitAndMergeSamples'));
console.log(SplitCamelCaseWithAbbreviations('PDFExtractorSDKSamples'));
console.log(SplitCamelCaseWithAbbreviations('PDFRendererSDKSamples'));
console.log(SplitCamelCaseWithAbbreviations('BarcodeReaderSDKSamples'));


3
这是目前最彻底的。该过滤器是为了去除空字符串。您应该在小写字符串上添加[0-9],但这绝对是最终答案。其他方法在字符串中间的缩写处失败了。 - RoboticRenaissance

5

我发现这个问题的所有答案都不能完全解决所有情况,而对于Unicode字符串根本没有用,所以这里提供一个可以处理一切(包括破折号和下划线表示法)的方法。

let samples = [
  "ThereIsWay_too  MuchCGIInFilms These-days",
  "UnicodeCanBeCAPITALISEDTooYouKnow",
  "CAPITALLetters at the StartOfAString_work_too",
  "As_they_DoAtTheEND",
  "BitteWerfenSie-dieFußballeInDenMüll",
  "IchHabeUberGesagtNichtÜber",
  "2BeOrNot2Be",
  "ICannotBelieveThe100GotRenewed. It-isSOOOOOOBad"
];

samples.forEach(sample => console.log(sample.replace(/([^[\p{L}\d]+|(?<=[\p{Ll}\d])(?=\p{Lu})|(?<=\p{Lu})(?=\p{Lu}[\p{Ll}\d])|(?<=[\p{L}\d])(?=\p{Lu}[\p{Ll}\d]))/gu, '-').toUpperCase()));

如果您不希望将数字视为小写字母,则:

let samples = [
  "2beOrNot2Be",
  "ICannotBelieveThe100GotRenewed. It-isSOOOOOOBad"
];

samples.forEach(sample => console.log(sample.replace(/([^\p{L}\d]+|(?<=\p{L})(?=\d)|(?<=\d)(?=\p{L})|(?<=[\p{Ll}\d])(?=\p{Lu})|(?<=\p{Lu})(?=\p{Lu}\p{Ll})|(?<=[\p{L}\d])(?=\p{Lu}\p{Ll}))/gu, '-').toUpperCase()));


5
如果您想将数字大写并在它们之间添加空格,可以使用以下方法。
transform(value: string, ...args: any[]): string {
    const str = 'this1IsASampleText';
    str.charAt(0).toUpperCase() + value.slice(1); // Capitalize the first letter
    str.replace(/([0-9A-Z])/g, ' $&'); // Add space between camel casing
}

结果:

This 1 Is A Sample Text    

1
Uncaught ReferenceError: value is not defined - kimbaudi
1
@kimbaudi 我已经更新了代码。感谢你的指出。 - Kegan VanSickle

3

你好,我看到了没有实时演示的示例,感谢@michiel-dral

var tests =[ "camelCase",
             "simple",
             "number1Case2",
             "CamelCaseXYZ",
             "CamelCaseXYZa" 
           ]

function getCamelCaseArray(camel) {
  var reg = /([a-z0-9])([A-Z])/g;
  return camel.replace(reg, '$1 $2').split(' ');
}

function printTest(test) {
document.write('<p>'+test + '=' + getCamelCaseArray(test)+'</p>');
}

tests.forEach(printTest);
<!DOCTYPE html>
<html>

  <head>
    <link rel="stylesheet" href="style.css">
    <script src="script.js"></script>
  </head>

  <body>
  </body>

</html>


'ThisIsASlug'.replace(/([a-z0-9])([A-Z])/g, '$1 $2').split(' ') 的输出结果为 ["This", "Is", "ASlug"] - kimbaudi

3

正则表达式非单词边界字符\B也可用。

console.log("MyCamelCaseString".replace(/(\B[A-Z])/g, ' $1'));


但是 'ExampleID'.replace(/(\B[A-Z])/g, ' $1') 给我的结果是 Example I D。我希望得到的结果是 Example ID - kimbaudi

2

如果你像我一样,有一个驼峰式命名的值,例如:

thisIsMyCamelCaseValue 其中第一个字母是小写的

function fromCamelCase(value) {
    const spaced = value.replace(/([a-z])([A-Z])/g, '$1 $2');
    return spaced.charAt(0).toUpperCase() + spaced.slice(1);
}

2
a = 'threeBlindMice'
a.match(/[A-Z]?[a-z]+/g) // [ 'three', 'Blind', 'Mice' ]

这是我发现的最简单的方法,用于简单的驼峰式/标题式拆分。

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