发布于 2026-01-06 1 阅读
0

使用正则表达式解析用户输入的 URL、时间戳和标签 🧠 由 Mux 呈现的 DEV 全球展示挑战赛:展示你的项目!

使用正则表达式解析用户输入的网址、时间戳和话题标签🧠

由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!

教程视频版📽

我以前总是尽量避免使用正则表达式(简称RegEx)。除了不理解它的用法之外,我也觉得它在我的代码里没什么用处。平心而论,正则表达式对新手确实不太友好。然而,我现在却开始寻找使用它的机会。事实上,正则表达式可以节省大量的开发时间,而且功能强大。

最近,我一直在研究如何重现YouTube的部分功能,并注意到视频描述和评论中一个简单却很巧妙的设计。用户可以输入网址、时间戳和话题标签,YouTube会解析这些输入并将其转换为链接。网址会变成外部链接,时间戳会变成指向视频特定位置的链接,而话题标签则会变成搜索词,方便用户找到相关内容。

youtube-描述

有很多不错的工具和网站可以用来学习正则表达式。有时候,直接谷歌搜索就能regex for <whatever>找到一些不错的Stack Overflow 帖子。RegExr就非常棒。你可以创建一个账户,并将你的表达式保存到你自己的库中。此外,他们还会解释每个字符及其作用,更不用说还有一个社区表达式数据库了。Regular Expressions Info 网站则提供了更详细的正则表达式相关内容的讲解,几乎涵盖了所有方面

本教程假设您已经捕获并存储了用户输入。这就是我们要解析的原始文本。接下来,我们需要处理一些事项,才能将文本处理成HTML

  1. 保留文本格式——空格、换行符等。
  2. 将文本放入HTML元素中
  3. 解析文本以提取网址、时间戳(HH:MM:SS格式)和话题标签
  4. 如有需要,请将这些替换为合适的链接、目标和参数。
  5. 附加功能:设置视频播放时间,并根据话题标签进行搜索

⚠ 免责声明 - 所有代码示例均使用React和/或JSX语法,因此均为JavaScript。

保留格式其实很简单。一种方法是使用HTML pre标签。pre我觉得 是 pre-formatted(预格式化)的缩写。🤦‍♂️

<pre>{description}</pre>
Enter fullscreen mode Exit fullscreen mode

另一种方法是使用white-spaceCSS 属性并将其设置为pre。我们不妨也使用pre-wrap。否则,过长的文本行会超出容器范围。

<div style={{whiteSpace: 'pre-wrap'}}>{description}</div>
Enter fullscreen mode Exit fullscreen mode

现在我们需要使出杀手锏🔫。首先,我们需要找到并大致理解其中涉及的正则表达式。这里有一个相当标准的查找http/sURL 的表达式。它基本上是查找 `<url>` http://anything,但似乎效果不错。注意 ` g--all` 标志匹配所有匹配项,以及忽略大小写的标志。它还可以通过在第一个捕获块中使用 OR 运算符来i匹配 ` <url>`ftp和`<url>`。file

const reUrl = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi
Enter fullscreen mode Exit fullscreen mode

时间戳表达式的情况还不算太糟。请注意,它(?:)?设置了一些可选的捕获组。这样做[0-5]是有道理的,因为在处理时间戳时,HH:MM:SS您不会看到01:90:90最大值,最高分钟或秒数可以是 59。总之,它的设置是为了匹配时间戳MM:SS:SS这很棒。这让用户在使用时间链接时拥有更大的灵活性。

const reTime = /\s(?:(?:([01]?\d):)?([0-5]?\d))?:([0-5]?\d)\s/g
Enter fullscreen mode Exit fullscreen mode

好了,我们来看看这个函数本身。我们将利用原型replace上的方法String。`String.prototype.replace`可以接受两个 参数:第一个参数是匹配的文本,第二个参数是一个函数。这个回调函数可以接收多个参数,但第一个参数是匹配的文本本身。这意味着我们可以在替换字符串中使用原始的 URL、时间或哈希值。我们的想法是用相应的HTML替换匹配项。为了简单起见,我们先从 URL 开始。这个过程通常被称为“替换过程”。明白了吗?🧠RegExlinkify

function linkify(text) {
    return text.replace(reUrl, url => `<a href="${url}" target="_blank">${url}</a>`)
}
Enter fullscreen mode Exit fullscreen mode

我使用了箭头函数并返回了一个模板字符串以节省空间。目标设置_blank确保此页面将在新窗口中打开。模板字符串你应该已经有所了解了。

处理时间戳稍微复杂一些。我们需要一个辅助函数和一些额外的逻辑才能使其发挥作用。假设我们有一个视频播放器,例如YouTube。我们希望以时间戳链接的HH:MM:SS形式显示时间戳,但需要将该值转换为秒,以便设置搜索参数并将其传递给播放器。HTML视频元素有一个名为currentTime`time` 的属性,用于获取/设置视频的秒数。我们还需要获取播放器在我们网站上的页面 URL。

function HHMMSStoSeconds(str) {
  var p = str.split(':')
  var s = 0
  var m = 1

  while (p.length > 0) {
    s += m * parseInt(p.pop(), 10)
    m *= 60
  }

  return s
}

function linkify(text) {
    const playerUrl = 'http://www.youtube.com/watch'
    return text.replace(reTime, time => {
        const seconds = HHMMSStoSeconds(time)
        return `<a href="${playerUrl}?time=${seconds}">{time}</a>`
    })
}
Enter fullscreen mode Exit fullscreen mode

顺便提一下,我真的很喜欢字符串转换成秒数的功能。好久没用while循环了。🤓

现在,当用户点击时间戳链接时,我们可以在React组件中实现一些巧妙的逻辑,将视频定位到链接中指定的时间点。


class Player extends React.Component {

    componentDidMount() {
        const params = new URLSearchParams(window.location.search)
        const time = params.get('time')
        if(time) {
            this.video.currentTime = time
        }
    }

    render() {
        return <video ref={el=>this.video = el} src={src} />
    }
}
Enter fullscreen mode Exit fullscreen mode

这看起来可能有点奇怪,因为我们习惯了使用路由库,但它确实有效。了解一下URLSearchParams。使用 `<a>`ref标签也至关重要。React 的一些特性允许我们访问底层 DOM 节点及其所有内置 API。React RefsHTML 视频/音频 DOM等都非常有用。

标签(hashtag)的工作方式与时间戳非常相似。开发者可以自行决定如何将其集成到用户界面中。YouTube 会搜索与标签相关的任何内容。用于匹配标签的表达式可能类似于这样。

const reHash = /(?:\s|^)?#[A-Za-z0-9\-\.\_]+(?:\s|$)/g
Enter fullscreen mode Exit fullscreen mode

这个问题其实很容易理解。但我们可以把它分解如下。

(?: // start of non-capture group
\s  // match space character
|   // logical OR
^   // beginning of string
)   // end non-capture group
?   // match 0 or 1 of preceding
#   // match # character
[]  // enclosed character set
A-Z // capital A through Z
a-z // lowercase a through z
0-9 // digits 0 through 9
\-  // \ is an escape character matches -
+   // requires 1 or more match of preceding token
$   // end of string 
Enter fullscreen mode Exit fullscreen mode

现在我们可以把所有东西整合到一个大函数里。当然,每个人的需求都不一样,但下面这个例子类似于 YouTube。这次我传递的是一个video对象。这只是其中一种方法。不过,在我的实现中,如果时间大于视频时长,我觉得创建时间戳链接意义不大。看看这个if/else代码块,通过将参数返回给回调函数,就好像我们忽略了那个特定的匹配项。值得一提。

import HHMMSStoSeconds from './above-this'

const reUrl = /(\b(https?):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi
const reTime = /\s(?:(?:([01]?\d):)?([0-5]?\d))?:([0-5]?\d)\s/g
const reHash = /(?:\s|^)?#[A-Za-z0-9\-\.\_]+(?:\s|$)/g
const frontend = 'https://www.youtube.com'

export default function linkify(video) {
  return (
    video.description
      .replace(reUrl, url => `<a href="${url}" target="_blank">${url}</a>`)
      .replace(reTime, time => {
        const secs = HHMMSStoSeconds(time)
        if (secs > video.duration) {
          return time
        } else {
          return `<a href="${frontend}/watch?id=${video.id}&t=${secs}">${time}</a>`
        }
      })
      .replace(
        reHash,
        hash => `<a href="${frontend}/search?term=${hash.replace('#', '').trim()}">${hash}</a>`
      )
  )
}

Enter fullscreen mode Exit fullscreen mode

如果你真的读到了这里,肯定学到了一些东西。我花了几乎一整天的时间才弄明白这些东西,而且我还得参考各种不同的网站和搜索结果。为什么不把它们都整理到一起呢?当然,肯定还有更高效或更全面的正则表达式。但这些对我来说已经足够用了。

解析器教程

克隆组件系列

我的YouTube频道

有了这个库,这一切都能为你完成。

文章来源:https://dev.to/benjaminadk/parse-user-input-for-urls-timestamps--hashtags-3dh4