likes
comments
collection
share

Node.js 实战:狗屁不通文章生成器写一些无意义的检讨、反思、罚抄,是我们从小到大都一直做的事情,我不知道这些东西到

作者站长头像
站长
· 阅读数 19

引言

写一些无意义的检讨、反思、罚抄,是我们从小到大都一直做的事情,我不知道这些东西到底有没有用处😭,也许唯一的用处就是浪费时间,我们虽然没有办法消灭它,但是我们有办法去绕开它🐍。这次我们将会完成一个Node.js的小项目——狗屁不通文章生成器,让我们通过这篇文章去学会如何对抗形式主义!

项目开始

主入口文件

index.js 文件是项目的主入口文件,负责加载语料库、生成文章、并将生成的文章保存到指定位置。

导入所需模块

const { generate } = require('./lib/generator.js');
const { createRandomPicker } = require('./lib/random.js');
const { loadCorpus, saveCorpus } = require('./lib/corpus.js');

这些都是我们自己定义的文件模块,将这些功能封装到了外部的文件,而不是直接写在 index.js入口文件,这样会使得我们的主入口文件更加简洁,便于维护和阅读!

其中这三个模块

  • generator.js:负责生成文章的逻辑。

  • random.js:包含生成随机数和随机选择的功能。

  • corpus.js:包含读取和保存语料库的功能。

定义存储的变量

let article = [];
let title = '';

article:用于存储生成的文章内容, title:用于存储生成的文章标题。

处理函数,调用其他模块功能

    const corpus = loadCorpus('corpus/data.json');

先调用 loadCorpus 函数,读取 corpus/data.json 文件中的内容,并将其解析为一个对象,赋值给 corpus 变量。

    title = createRandomPicker(corpus.title)();

调用 createRandomPicker 函数,生成一个随机选择器,然后调用该选择器,从 corpus.title 中随机选择一个标题,并赋值给 title 变量。

article.push(title);

再将生成的标题添加到 article 数组中,确保标题是文章的第一个元素。

    article.push(...generate(title, { corpus }));

调用 generate 函数,生成文章内容,并将生成的内容添加到 article 数组中。

导出模块

module.exports = {
    article,
    handle,
    title
};

最后我们将这些在主入口函数获取到值的变量抛出,方便后续的调用。

标题生成

我们在lib目录下创建一个random.js用来随机从语料库中选取句子组成段落。

function randomInt(min,max){
        const n = Math.random()
        // 生成一个介于最小字数和最大字数之间的字数
        return Math.floor(min*(1-n)+max*n)  
}

function createRandomPicker(arr){
    // 使用闭包,相当于新建一个数组,再对数组进行操作
   arr = [...arr]
    
   // 保证连续两次取到的不一样
   // 每次将取出的序号与最后一位对调位置,且每次取出的都是上一次的最后一个
    function randomPick() {
       const len = arr.length-1
       const index = randomInt(0,len);
       [arr[index],arr[len]] = [arr[len],arr[index]]
       return arr[index]
    }
    // 先调用一次放弃掉,使得每次第一次调用出的不一定是原数组的最后一位
    randomPick()
    return randomPick

}

module.exports = {
    randomInt,
    createRandomPicker
}

首先实现一个生成随机数的函数

function randomInt(min, max) {
    const n = Math.random();
    // 生成一个介于最小值和最大值之间的随机整数
    return Math.floor(min * (1 - n) + max * n);
}

min * (1 - n) + max * n 通过加权计算生成一个介于 minmax 之间的浮点数。Math.floor() 将生成的浮点数向下取整,得到一个整数。

然后我们再去实现一个去从语料库中随机取出要的标题的函数

function createRandomPicker(arr) {
    // 使用闭包,相当于新建一个数组,再对数组进行操作
    arr = [...arr];
    
    // 保证连续两次取到的不一样
    // 每次将取出的序号与最后一位对调位置,且每次取出的都是上一次的最后一个
    function randomPick() {
        const len = arr.length - 1;
        const index = randomInt(0, len);
        [arr[index], arr[len]] = [arr[len], arr[index]];
        return arr[index];
    }

    // 先调用一次放弃掉,使得每次第一次调用出的不一定是原数组的最后一位
    randomPick();
    return randomPick;
}

arr = [...arr]:使用扩展运算符创建 arr 的副本,以免修改原数组。然后定义 randomPick 函数用于随机选择数组中的元素。

[arr[index], arr[len]] = [arr[len], arr[index]]:交换 arr[index]arr[len] 的位置,以保证连续两次选择的元素不同,相当于每次取出最末尾的元素,然后再将最末尾的换到前面,再依此往复,相必这里大家会有疑惑,第一个被选取的不都是最后一个元素吗,所以我们在后面加上了randomPick(),先调用一次 randomPick,使得第一次调用返回的元素不是原数组的最后一个元素。

最后达成了返回一个随机选择器函数 randomPick,用于从数组 arr 中随机选择元素,并保证连续两次选择的元素不同。

文章内容生成

我们定义 generatesentence 函数,用于生成文章内容并进行文本替换,然后将它们导出,以便在其他文件中使用。

const {randomInt, createRandomPicker} = require('./random.js');

// 生成文章
function generate(title, { corpus, min = 1000, max = 2000 }) {
    const articleLength = randomInt(min, max);
    const { famous, bosh_before, bosh, conclude, said } = corpus;

    // 使用 map 循环对五个数组生成随机选择器
    const [pickFamous, pickBoshBefore, pickBosh, pickConclude, pickSaid] = 
        [famous, bosh_before, bosh, conclude, said].map(createRandomPicker);

    const article = [];
    let totalLength = 0;

    while (totalLength < articleLength) {
        let section = '';
        const sectionLength = randomInt(100, 500);

        while (section.length < sectionLength) {
            const n = randomInt(0, 100);
            if (n < 20) { // 名人名言
                section += sentence(pickFamous, { said: pickSaid, conclude: pickConclude });
            } else if (n < 50) { // 前置废话
                section += sentence(pickBoshBefore, { title }) + sentence(pickBosh, { title });
            } else { // 废话
                section += sentence(pickBosh, { title });
            }
        }

        totalLength += section.length;
        article.push(section);
    }

    return article;
}

我们首先用const { famous, bosh_before, bosh, conclude, said } = corpus解构语料库对象,提取出五种类型的句子。

const [pickFamous, pickBoshBefore, pickBosh, pickConclude, pickSaid] = [famous, bosh_before, bosh, conclude, said].map(createRandomPicker) 这是一种解构赋值语法和map的方法。

解构赋值是一种从数组或对象中提取数据的简洁语法。通过解构赋值,我们可以将数组或对象中的元素直接赋值给变量。map 方法用于创建一个新数组,其结果是对原数组的每个元素调用一个提供的函数后的返回值。

通过这一行代码我们优雅地实现了:

  • corpus 对象中解构出 famousbosh_beforeboshconcludesaid 这五个数组。

  • 将这五个数组组成一个新的数组 [famous, bosh_before, bosh, conclude, said]

  • 使用 map 方法对这个新数组中的每一个元素(即每一个数组)应用 createRandomPicker 函数。createRandomPicker 函数会返回一个新的函数,这个新函数是一个随机选择器,用于从对应的数组中随机选择一个元素。

  • map 方法返回的新数组(包含五个随机选择器函数)解构赋值给 pickFamouspickBoshBeforepickBoshpickConcludepickSaid 这五个变量。

然后我们用了 while 循环生成文章的段落,直到 totalLength 达到 articleLength, 根据随机数 n 的值决定插入哪种类型的句子(名人名言、前置废话、废话)。 生成句子时调用 sentence 函数进行文本替换。 将生成的段落添加到 article 数组中,并更新 totalLength

随后是 sentence 函数

// 文本替换
function sentence(pick, replacer) {
    let res = pick();

    for (let key in replacer) {
        // 使用正则表达式替换模板中的占位符
        res = res.replace(
            new RegExp(`{{${key}}}`, 'g'),
            typeof replacer[key] === 'function' ? replacer[key]() : replacer[key]
        );
    }

    return res;
}
  • 调用 pick() 函数获取一个随机句子 res

  • 使用 for...in 循环遍历 replacer 对象的键:

    • 对每个键,创建一个新的正则表达式 new RegExp({{${key}}}, 'g') 用于匹配模板中的占位符。
    • 使用 replace 方法将占位符替换为 replacer[key] 对应的值,如果 replacer[key] 是一个函数则调用它,否则直接使用其值。

将生成的文章显示到页面

这里我们将使用 Node.js 的一个核心模块 http,我们先导入

const http = require('http');
const fs = require('fs');
const { article, handle, title } = require('./index.js');

handle();

这里的handleindex.js中抛出的,所以我们这里调用会生成一篇文章并存在 article中。

然后再去创建一个 HTTP 服务器

const server = http.createServer((req, res) => {
   if (req.url === '/') {
      let html = fs.readFileSync(__dirname + '/index.html', 'utf-8'); // Change const to let
      const title = article[0]; // Assuming the title is the first element in the article array
      const content = article.slice(1).join('<br/>'); // Rest of the article
      html = html.replace('{{title}}', title).replace('{{article}}', content);
      res.writeHead(200, { 'Content-Type': 'text/html' });
      res.end(html);
  } else {
      res.writeHead(404, { 'Content-Type': 'text/plain' });
      res.end('Not Found');
  }
});

如果请求的 URL 是 /(根路径),则:

  • 使用 fs.readFileSync 同步读取 index.html 文件的内容,并将其编码为 UTF-8 字符串。使用 let 声明 html 变量来存储 HTML 内容。
  • 假设 article 数组的第一个元素是文章的标题,将其赋值给 title 变量。
  • 使用 article.slice(1).join('<br/>')article 数组中的剩余元素(即文章内容)连接成一个字符串,每个段落用 <br/> 标签分隔。
  • 使用 html.replace 方法将 {{title}}{{article}} 占位符替换为实际的文章标题和内容。
  • 设置响应头,指明响应内容的类型为 text/html
  • 使用 res.end 方法发送响应,将生成的 HTML 返回给客户端。

如果请求的 URL 不是 /,则: 设置响应头,指明响应内容的类型为 text/plain。 使用 res.end 方法发送 404 响应,指明请求的资源未找到。

最后启动服务器

server.listen(3000, () => {
    console.log('Server is running at http://localhost:3000');
});

使用 server.listen 方法启动服务器,监听 3000 端口。 启动后,服务器会输出一条消息提醒。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>狗屁不通文章生成器</title>
</head>
<body>
    <h2>{{title}}</h2>
    <article>{{article}}</article>
</body>
</html>

效果展示

Node.js 实战:狗屁不通文章生成器写一些无意义的检讨、反思、罚抄,是我们从小到大都一直做的事情,我不知道这些东西到

Node.js 实战:狗屁不通文章生成器写一些无意义的检讨、反思、罚抄,是我们从小到大都一直做的事情,我不知道这些东西到 我们的狗屁不通文章生成器便完成了!

总结

通过这个项目,我们不仅实现了一个简易的文章生成器,还深入理解了 Node.js 文件系统操作、随机数生成、文本处理和 HTTP 服务器搭建的基础知识。这种项目在实际应用中有广泛的前景,无论是用于生成娱乐内容,还是辅助生成初步的文档,都有很大的实用价值。如果这篇文章对你有帮助,可以点个赞哦😊!

转载自:https://juejin.cn/post/7397816165207621658
评论
请登录