将驼峰式字符串转换为短横线式字符串的正则表达式

4
function hyphenate(str) {

  var replace = "-";
  str = str.toLowerCase().replace(/[\s_\b]/g, replace);

  console.log(str);
  return str;
}

hyphenate("This Is Hyphenate"); // this-is-hyphenate
hyphenate("camelCaseString");   // camel-case-string

我正在努力让我的代码产生第二个函数调用的结果,但是我没有找到可以实现这一点的模式。非常感谢您的帮助。
4个回答

6
请注意,在你的[\s_\b]中,\b表示回退(backspace)字符。不确定你是否真正需要使用它。
使用ECMAScript 2018引入的后顾断言功能更新答案:

const re = /[\W_]+|(?<=[a-z0-9])(?=[A-Z])/g;
const strs = ['camelCaseString','This      Is Hyphenate','This_Should_Hyphenate', '09Report'];
strs.forEach(str => 
  console.log( str.replace(re, "-").toLowerCase() )
);

[\W_]+|(?<=[a-z0-9])(?=[A-Z])正则表达式将匹配:
  • [\W_]+ - 任意1个或多个非单词和下划线字符
  • | - 或
  • (?<=[a-z0-9])(?=[A-Z]) - 大写ASCII字母和小写ASCII字母/数字之间的位置。

旧答案

我会使用稍微不同的逻辑:在单词内的每个大写字母前添加连字符,然后替换并转换为小写:

var re = /[\s_]+|([a-z0-9])(?=[A-Z])/g; 
var str = 'camelCaseString<br/>This      Is Hyphenate<br/>This_Should_Hyphenate';
var result = str.replace(re, "$1-").toLowerCase();
document.body.innerHTML += result;

说明:

  • [\s_]+ - 一个或多个空格或下划线
  • | - 或...
  • ([a-z0-9]) - (第1组)小写字母或数字(由于\B不能让我们在_后匹配大写字母,如果您想在每个大写字母前添加-,请添加A-Z
  • (?=[A-Z]) - 检测是否有一个大写的ASCII字母(这不会被捕获,因为(?=[A-Z])是一个先行断言,是一个零宽度断言)。

抱歉,我之前想说的是如何处理这样的格式:“This_Should_Hyphenate”。 - brndng
下划线的问题在于它被视为一个单词字符。我添加了一个修复方案。 - Wiktor Stribiżew
2
非常感谢您提供了一些好的线索,让我可以学习更多关于正则表达式模式的知识。我非常感激,因为我正在尽可能多地学习。所以本质上,前瞻([a-z0-9])(?= [A-Z])仅在其后跟随大写字母时匹配([a-z0-9])?我希望我正确理解了这个逻辑。"$1-"会在前瞻匹配后面放置一个连字符吗? - brndng
@brndng 1)是的,仅当紧接其后有大写字母时才会出现。2)“-”将插入整体匹配值之后,并且前瞻匹配值不会添加到该值中,因为它的行为是非消耗性的,它不会将其匹配添加到整体匹配值中并且不会推进正则表达式索引。 - Wiktor Stribiżew

2

在转换为小写之前尝试向前查看:

function hyphenate(str) {
  return str.split(/[\s_\b]|(?=[A-Z])/).join('-').toLowerCase();
}

这是一个非常优雅的答案,我需要对前瞻进行一些研究。 - brndng

1
你可以使用捕获组获取小写字母后面的大写字母,然后将整个字符串转换为小写:
str.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();

0

这可能超出了您的要求,但希望这个答案能帮助任何试图将(几乎)任何字符串转换为 kebab case 的人:

const convertStringToKebebCase = str => str && str
  .match(/[0-9]{1,}(?=\b)|[A-Z]{2,}(?=[A-Z][a-z]+|[0-9]|\b|_)|[A-Z]?[a-z]+|[A-Z]|[0-9]+/g)
  .map(x => x.toLowerCase())
  .join('-')

以下是上述函数的测试,这样您就可以了解它的行为(我将函数重命名为toKebeb,只是为了更容易阅读):

// Lowercase
expect(toKebeb('path')).toEqual('path')
expect(toKebeb('PATH')).toEqual('path')

// Spaces
expect(toKebeb('path route')).toEqual('path-route')
expect(toKebeb('path route 0')).toEqual('path-route-0')
expect(toKebeb('123 path 4 route 567')).toEqual('123-path-4-route-567')

// Kebab
expect(toKebeb('path-route')).toEqual('path-route')
expect(toKebeb('PATH-ROUTE')).toEqual('path-route')
expect(toKebeb('path-route0')).toEqual('path-route-0')
expect(toKebeb('path-route-0')).toEqual('path-route-0')
expect(toKebeb('123-path-4-route-567')).toEqual('123-path-4-route-567')
expect(toKebeb('123-path-4-route-567')).toEqual('123-path-4-route-567')

// Snake
expect(toKebeb('path_route')).toEqual('path-route')
expect(toKebeb('PATH_ROUTE')).toEqual('path-route')
expect(toKebeb('path_route0')).toEqual('path-route-0')
expect(toKebeb('path_route_0')).toEqual('path-route-0')
expect(toKebeb('123_path_4_route_567')).toEqual('123-path-4-route-567')
expect(toKebeb('123_path_4_route_567')).toEqual('123-path-4-route-567')

// Camel
expect(toKebeb('pathRoute')).toEqual('path-route')
expect(toKebeb('pathROUTE')).toEqual('path-route')
expect(toKebeb('pathRoute0')).toEqual('path-route-0')
expect(toKebeb('pathROUTE0')).toEqual('path-route-0')
expect(toKebeb('123path4Route567')).toEqual('123-path-4-route-567')
expect(toKebeb('123path4ROUTE567')).toEqual('123-path-4-route-567')
expect(toKebeb('pathRouteA')).toEqual('path-route-a')
expect(toKebeb('pathRouteABC')).toEqual('path-route-abc')
expect(toKebeb('pathIsARoute')).toEqual('path-is-a-route')

// Other
expect(toKebeb('path-route0')).toEqual('path-route-0')
expect(toKebeb('path-route123')).toEqual('path-route-123')
expect(toKebeb('path1route')).toEqual('path-1-route')
expect(toKebeb('path123route')).toEqual('path-123-route')
expect(toKebeb('123pathRoute')).toEqual('123-path-route')
expect(toKebeb('123PATHRoute')).toEqual('123-path-route')
expect(toKebeb('123pathROUTE')).toEqual('123-path-route')

我提到这个函数可以转换几乎任何字符串,这是因为处理数字的方式可能因每个用例而异。例如,期望3dPrinter返回3d-printer是完全合理的。可以调整正则表达式以支持此功能,但会引发其他问题,例如如何处理3dPrinter12my3dPrinterse7en(即哪些数字-字符串顺序组合受到尊重)。支持这些规则将大大增加所需的测试数量,并且总会有例外。

为了支持3dPrinter示例,您可以在正则表达式的开头(在“/”之后)添加[0-9]{1,}[a-z]{1,}(?=[A-Z]+)|,但这会破坏一些早期的规则。

要了解此正则表达式的工作原理,请查看regexr上的模式。


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