主頁 > 後端開發 > 說說驗證碼功能的實作

說說驗證碼功能的實作

2023-06-07 07:42:41 後端開發

前言

大家好,我是 god23bin,今天說說驗證碼功能的實作,相信大家都經常接觸到驗證碼的,畢竟平時上網也能遇到各種驗證碼,需要我們輸入驗證碼進行驗證我們是人類,而不是機器人,

驗證碼有多種型別,比如圖片驗證碼、短信驗證碼和郵件驗證碼等等,雖說多種型別,圖片也好,短信也好,郵件也好,都是承載驗證碼的載體,最主要的核心就是一個驗證碼的生成、存盤和校驗,

本篇文章就從這幾個方面出發說說驗證碼,廢話不多說,下面開始正文,

實作思路

驗證碼驗證的功能,其實作思路還是挺簡單的,不論是圖片驗證碼、短信驗證碼還是郵件驗證碼,無非就以下幾點:

  1. 驗證碼本質就是一堆字符的組合(數字也好,英文字母也好),后端生成驗證碼,并存盤到某個位置(比如存盤到 Redis,并設定驗證碼的過期時間),
  2. 回傳驗證碼給前端頁面、發送短信驗證碼給用戶或者發送郵件驗證碼給用戶,驗證碼可以是以文字顯示或者圖片顯示,
  3. 用戶輸入看到的驗證碼,并提交驗證(驗證也可以忽略大小寫,當然具體看需求),
  4. 后端將用戶輸入的驗證碼拿過來進行校驗,對比用戶輸入的驗證碼是否和后端生成的一致,一致就驗證成功,否則驗證失敗,

驗證碼的生成

首先,需要知道的就是驗證碼的生成,這就涉及到生成驗證碼的演算法,可以自己純手寫,也可以使用人家提供的工具,這里我就介紹下面 4 種生成驗證碼的方式,

1. 純原生手寫生成文本驗證碼

需求:隨機產生一個 n 位的驗證碼,每位可能是數字、大寫字母、小寫字母,

實作:本質就是隨機生成字串,字串可包含數字、大寫字母、小寫字母,

準備一個包含數字、大寫字母、小寫字母的字串,借助 Random 類,回圈 n 次隨機獲取字串的下標,就能拼接出一個隨機字符組成的字串了,

package cn.god23bin.demo.util;

import java.util.Random;

public class MyCaptchaUtil {

	/**
     * 生成 n 位驗證碼
     * @param n 位數
     * @return n 位驗證碼
     **/
    public static String generateCode(int n) {
        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < n; i++) {
            int index = random.nextInt(chars.length());
            sb.append(chars.charAt(index));
        }
        return sb.toString();
    }
    
}

2. 純原生手寫生成圖片驗證碼

實作:使用 Java 的 awt 和 swing 庫來生成圖片驗證碼,下面使用 BufferedImage 類創建一個指定大小的圖片,然后隨機生成 n 個字符,將其畫在圖片上,將生成的字符和圖片驗證碼放到哈希表回傳,后續我們就可以拿到驗證碼的文本值,并且可以將圖片驗證碼輸出到指定的輸出流中,

package cn.god23bin.demo.util;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;

public class MyCaptchaUtil {

	/**
     * 生成 n 位的圖片驗證碼
     * @param n 位數
     * @return 哈希表,code 獲取文本驗證碼,img 獲取 BufferedImage 圖片物件
     **/
    public static Map<String, Object> generateCodeImage(int n) {
        int width = 100, height = 50;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();
        g.setColor(Color.LIGHT_GRAY);
        g.fillRect(0, 0, width, height);
        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            int index = random.nextInt(chars.length());
            char c = chars.charAt(index);
            sb.append(c);
            g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
            g.setFont(new Font("Arial", Font.BOLD, 25));
            g.drawString(Character.toString(c), 20 + i * 15, 25);
        }
        Map<String, Object> res = new HashMap<>();
        res.put("code", sb.toString());
        res.put("img", image);
        return res;
    }
    
}

我們可以寫一個獲取驗證碼的介面,以二進制流輸出回傳給前端,前端可以直接使用 img 標簽來顯示我們回傳的圖片,只需在 src 屬性賦值我們的獲取驗證碼介面,

@RequestMapping("/captcha")
@RestController
public class CaptchaController {

    @GetMapping("/code/custom")
    public void getCode(HttpServletResponse response) {
        Map<String, Object> map = MyCaptchaUtil.generateCodeImage(5);
        System.out.println(map.get("code"));
        BufferedImage img = (BufferedImage) map.get("img");

        // 設定回應頭,防止快取
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/png");
        try {
            ImageIO.write(img, "png", response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

3. 使用 Hutool 工具生成圖形驗證碼

引入依賴:可以單獨引入驗證碼模塊或者全部模塊都引入

<!-- 驗證碼模塊 -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-captcha</artifactId>
    <version>5.8.15</version>
</dependency>

<!-- 全部模塊都引入 -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.15</version>
</dependency>
  • 生成線段干擾的驗證碼:
// 設定圖形驗證碼的寬和高,同時生成了驗證碼,可以通過 lineCaptcha.getCode() 獲取文本驗證碼
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100);
  • 生成圓圈干擾的驗證碼:
// 設定圖形驗證碼的寬、高、驗證碼字符數、干擾元素個數
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 20);
  • 生成扭曲干擾的驗證碼:
// 定義圖形驗證碼的寬、高、驗證碼字符數、干擾線寬度
ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(200, 100, 4, 4);

image-20230606231536066

獲取驗證碼介面:

@RequestMapping("/captcha")
@RestController
public class CaptchaController {

    @GetMapping("/code/hutool")
    public void getCodeByHutool(HttpServletResponse response) {
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100);
        System.out.println("線段干擾的驗證碼:" + lineCaptcha.getCode());

        // 設定回應頭,防止快取
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/png");
        try {
            lineCaptcha.write(response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

4. 使用 Kaptcha 生成驗證碼

Kaptcha 是谷歌的一個生成驗證碼工具包,我們簡單配置其屬性就可以實作驗證碼的驗證功能,

引入依賴項:它只有一個版本:2.3.2

<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>

簡單看看 kaptcha 屬性:

屬性 描述 默認值
kaptcha.border 圖片邊框,合法值:yes , no yes
kaptcha.border.color 邊框顏色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. black
kaptcha.border.thickness 邊框厚度,合法值:>0 1
kaptcha.image.width 圖片寬 200
kaptcha.image.height 圖片高 50
kaptcha.producer.impl 圖片實作類 com.google.code.kaptcha.impl.DefaultKaptcha
kaptcha.textproducer.impl 文本實作類 com.google.code.kaptcha.text.impl.DefaultTextCreator
kaptcha.textproducer.char.string 文本集合,驗證碼值從此集合中獲取 abcde2345678gfynmnpwx
kaptcha.textproducer.char.length 驗證碼長度 5
kaptcha.textproducer.font.names 字體 Arial, Courier
kaptcha.textproducer.font.size 字體大小 40px
kaptcha.textproducer.font.color 字體顏色,合法值: r,g,b 或者 white,black,blue. black
kaptcha.textproducer.char.space 文字間隔 2
kaptcha.noise.impl 干擾實作類 com.google.code.kaptcha.impl.DefaultNoise
kaptcha.noise.color 干擾顏色,合法值: r,g,b 或者 white,black,blue. black
kaptcha.obscurificator.impl 圖片樣式: 水紋com.google.code.kaptcha.impl.WaterRipple 魚眼com.google.code.kaptcha.impl.FishEyeGimpy 陰影com.google.code.kaptcha.impl.ShadowGimpy com.google.code.kaptcha.impl.WaterRipple
kaptcha.background.impl 背景實作類 com.google.code.kaptcha.impl.DefaultBackground
kaptcha.background.clear.from 背景顏色漸變,開始顏色 light grey
kaptcha.background.clear.to 背景顏色漸變,結束顏色 white
kaptcha.word.impl 文字渲染器 com.google.code.kaptcha.text.impl.DefaultWordRenderer
kaptcha.session.key session key KAPTCHA_SESSION_KEY
kaptcha.session.date session date KAPTCHA_SESSION_DATE

簡單配置下 Kaptcha:

package cn.god23bin.demo.config;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Properties;

@Configuration
public class KaptchaConfig {
    /**
     * 配置生成圖片驗證碼的bean
     * @return
     */
    @Bean(name = "kaptchaProducer")
    public DefaultKaptcha getKaptchaBean() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        properties.setProperty("kaptcha.border", "no");
        properties.setProperty("kaptcha.textproducer.font.color", "black");
        properties.setProperty("kaptcha.textproducer.char.space", "4");
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        properties.setProperty("kaptcha.textproducer.char.string", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

也是和 Hutool 一樣,很簡單就能生成驗證碼了,如下:

// 生成文字驗證碼
String text = kaptchaProducer.createText();
// 生成圖片驗證碼
BufferedImage image = kaptchaProducer.createImage(text);

獲取驗證碼介面:

@RequestMapping("/captcha")
@RestController
public class CaptchaController {

    @Autowired
    private Producer kaptchaProducer;

    @GetMapping("/code/kaptcha")
    public void getCodeByKaptcha(HttpServletResponse response) {
        // 生成文字驗證碼
        String text = kaptchaProducer.createText();
        System.out.println("文字驗證碼:" + text);
        // 生成圖片驗證碼
        BufferedImage image = kaptchaProducer.createImage(text);

        // 設定回應頭,防止快取
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/jpeg");
        try {
            ImageIO.write(image, "jpg", response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

驗證碼的存盤與校驗

上面的驗證碼的生成,就僅僅是生成驗證碼,并沒有將驗證碼存盤在后端,所以現在我們需要做的是:將驗證碼存盤起來,便于后續的校驗對比,

那么存盤到什么地方呢?如果你沒接觸過 Redis,那么第一次的想法可能就是存盤到關系型資料庫中,比如 MySQL,想當年,我最開始的想法就是這樣哈哈哈,

不過,目前用得最多的就是將驗證碼存盤到 Redis 中,好處就是減少了資料庫的壓力,加快了驗證碼的讀取效率,還能輕松設定驗證碼的過期時間,

簡單配置 Redis

引入 Redis 依賴項:

我們使用 Spring Data Redis,它提供了 RedisTemplateStringRedisTemplate 模板類,簡化了我們使用 Java 進行 Redis 的操作,

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

簡單配置下 Redis:

spring:
  redis:
    host: localhost
    port: 6379
    database: 1
    timeout: 5000
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 大多數情況,都是選用<String, Object>
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        // 使用JSON的序列化物件,對資料 key 和 value 進行序列化轉換
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        // ObjectMapper 是 Jackson 的一個作業類,作用是將 JSON 轉成 Java 物件,即反序列化,或將 Java 物件轉成 JSON,即序列化
        ObjectMapper mapper = new ObjectMapper();
        // 設定序列化時的可見性,第一個引數是選擇序列化哪些屬性,比如時序列化 setter? 還是 filed? 第二個引數是選擇哪些修飾符權限的屬性來序列化,比如 private 或者 public,這里的 any 是指對所有權限修飾的屬性都可見(可序列化)
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        jackson2JsonRedisSerializer.setObjectMapper(mapper);
        // 設定 RedisTemplate 模板的序列化方式為 jacksonSeial
        template.setDefaultSerializer(jackson2JsonRedisSerializer);
        return template;
    }
    
}

將驗證碼存盤到 Redis

將驗證碼存盤到 Redis 設定 5 分鐘的過期時間,Redis 是 Key Value 這種形式存盤的,所以需要約定好 Key 的命名規則,

命名的時候,為了區分為每個用戶生成的驗證碼,所以需要一個標識,剛好可以通過當前請求的 HttpSession 中的 SessionID 作為唯一標識,拼接到 Key 的名稱中,

當然,也不一定使用 SessionID 作為唯一標識,如果能知道其他的,也可以用其他的作為標識,比如拼接用戶的手機號,

實作:

@RequestMapping("/captcha")
@RestController
public class CaptchaController {

    @Autowired
    private Producer kaptchaProducer;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @GetMapping("/code")
    public void getCode(HttpServletRequest request, HttpServletResponse response) {
        // 生成文字驗證碼
        String text = kaptchaProducer.createText();
        System.out.println("文字驗證碼:" + text);
        // 生成圖片驗證碼
        BufferedImage image = kaptchaProducer.createImage(text);

        // 存盤到 Redis 設定 5 分鐘的過期時間
        // 約定好存盤的 Key 的命名規則,這里使用 code_sessionId_type_1 表示圖形驗證碼
        // Code_sessionId_Type_1:分為 3 部分,code 表明是驗證碼,sessionId 表明是給哪個用戶的驗證碼,type_n 表明驗證碼型別,n 為 1 表示圖形驗證碼,2 表示短信驗證碼,3 表示郵件驗證碼
        String key = "code_" + request.getSession().getId() + "_type_1";
        redisTemplate.opsForValue().set(key, text, 5, TimeUnit.SECONDS);

        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/jpeg");
        try {
            ImageIO.write(image, "jpg", response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

上面代碼中有一個額外的設計就是,由于發送的驗證碼有多種型別(圖形驗證碼、短信驗證碼、郵件驗證碼),所以加多了一個 type_n 來標識當前存盤的驗證碼是什么型別的,方便以后出現問題快速定位,

實際上,這里的命名規則,可以根據你的具體需求來定制,又比如說,登錄的時候需要驗證碼、注冊的時候也需要驗證碼、修改用戶密碼的時候也需要驗證碼,為了便于出現問題進行定位,也可以繼續加多一個標識 when_n,n 為 1 表示注冊、n 為 2 表示登錄,以此類推,

校驗

我們模擬登錄的時候進行驗證碼的校驗,使用一個 LoginDTO 物件來接收前端的登錄相關的引數,

package cn.god23bin.demo.model.domain.dto;

import lombok.Data;

@Data
public class LoginDTO {
    private String username;
    private String password;
    /**
     * 驗證碼
     */
    private String code;
}

寫一個登錄介面,登錄的程序中,校驗用戶輸入的驗證碼,

@RequestMapping("/user")
@RestController
public class UserController {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @PostMapping("/login")
    public Result<String> login(@RequestBody LoginDTO loginDTO, HttpServletRequest request) {
        if (!"root".equals(loginDTO.getUsername()) || !"123456".equals(loginDTO.getPassword())) {
            return Result.fail("登錄失敗!賬號或密碼不正確!");
        }
        // 校驗用戶輸入的驗證碼
        String code = loginDTO.getCode();
        String codeInRedis = (String) redisTemplate.opsForValue().get("code_" + request.getSession().getId() + "_type_1");
        if (!code.equals(codeInRedis)) {
            return Result.fail("驗證碼不正確!");
        }
        return Result.ok("登錄成功!");
    }
}

至此,便完成了驗證碼功能的實作,

獲取驗證碼的安全設計

驗證碼功能的實作現在是OK的,但還有一點需要注意,那就是防止驗證碼被隨意呼叫獲取,或者被大量呼叫,如果不做限制,那么誰都能呼叫,就非常大的可能會被攻擊了,

我們上面實作的驗證碼功能是圖形驗證碼,是校驗用戶從圖形驗證碼中看到后輸入的數字字母組合跟后端生成的組合是否是一致的,對于圖形驗證碼,到這里就可以了,不用限制(當然想限制也可以),但是對于短信驗證碼,就還不可以,我們需要額外考慮一些防刷機制,以保障系統的安全性和可靠性(因為發短信是要錢的啊!),

對于短信來說,一種常見的攻擊方式是「短信轟炸」,攻擊者通過自動批量提交手機號碼、模擬IP等手段,對系統進行大規模的短信請求,從而消耗資源或干擾正常業務,為了應對這種情況,我們需要設計一些防刷機制,

防刷機制

目前我了解到的防刷機制有下面幾種,如果你有別的方法,歡迎評論說出來噢!

  1. 圖形驗證碼或者滑動驗證:發送短信前先使用圖形驗證碼或者滑動進行驗證,驗證成功才能呼叫發送短信驗證碼的介面,
  2. 時間限制:從用戶點擊發送短信驗證碼開始,前端進行一個 60 秒的倒數,在這 60 秒之內,用戶無法提交發送資訊的請求的,這樣就限制了發送短信驗證碼的介面的呼叫次數,不過這種方式,如果被攻擊者知道了發送短信的介面,那也是會被刷的,
  3. 手機號限制:對使用同一個手機號碼進行注冊或者其他發送短信驗證碼的操作的時候,系統可以對這個手機號碼進行限制,例如,一天只能發送 5 條短信驗證碼,超出限制則做出提示(如:系統繁忙,請稍后再試),然而,這也只能夠避免人工手動刷短信而已,對于批量使用不同手機號碼來刷短信的機器,同樣是會被刷,
  4. IP地址限制:記錄請求的IP地址,并對同一 IP 地址的請求進行限制,比如限制某個 IP 地址在一定時間內只能發送特定數量的驗證碼,同樣,也是可以被轟炸的,

至于這些機制的實作,有機會再寫寫,你感興趣的話可以自己去操作試試!

總結

本篇文字就說了驗證碼功能的實作思路和實作,包括驗證碼的生成、存盤、展示和校驗,

  • 生成驗證碼可以手寫也可以借助工具,

  • 存盤一般是存盤在 Redis 中的,當然你想存盤在 MySQL 中也不是不可以,就是需要自己去實作諸如過期時間的功能,

  • 展示可以通過文本展示或者圖片展示,我們可以回傳一個二進制流給前端,前端通過 img 標簽的 src 屬性去請求我們的介面,

  • 校驗就拿到用戶輸入的驗證碼,和后端生成的驗證碼進行比對,相同就驗證成功,否則失敗,

最后我們也說了驗證碼的防刷機制,這是需要考慮的,這里的防刷機制對于使用大量不同手機號、不同 IP 地址是沒效果的,依舊可以暴刷,所以這部分內容還是有待研究的,也歡迎大家在評論區說出你的看法!

最后的最后

希望各位螢屏前的靚仔靚女們給個三連!你輕輕地點了個贊,那將在我的心里世界增添一顆明亮而耀眼的星!

咱們下期再見!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/554488.html

標籤:其他

上一篇:演算法 in Golang:Breadth-first search(BFS、廣度優先搜索)

下一篇:返回列表

標籤雲
其他(160489) Python(38206) JavaScript(25478) Java(18205) C(15237) 區塊鏈(8270) C#(7972) AI(7469) 爪哇(7425) MySQL(7234) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5873) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4585) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2434) ASP.NET(2403) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1983) 功能(1967) HtmlCss(1952) Web開發(1951) C++(1932) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1879) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • 說說驗證碼功能的實作

    大家好,我是 god23bin,今天說說驗證碼功能的實作,相信大家都經常接觸到驗證碼的,畢竟平時上網也能遇到各種驗證碼,需要我們輸入驗證碼進行驗證我們是人類,而不是機器人。 ......

    uj5u.com 2023-06-07 07:42:41 more
  • 演算法 in Golang:Breadth-first search(BFS、廣度優先搜索)

    # 演算法 in Golang:Breadth-first search # (BFS、廣度優先搜索) ## 最短路徑問題 Shortest-path problem - 從 A 到 F 點有多條路徑 ## 解決問題的演算法 Breadth-first Search(廣度優先搜索) 1. 將問題建模為圖 ......

    uj5u.com 2023-06-07 07:42:35 more
  • 逍遙自在學C語言 | break-回圈的中斷與跳轉

    ## 前言 在C語言中,`break`陳述句是一種控制流陳述句,它用于終止當前所在的回圈結構(`for`、`while`、`do-while`)或者`switch`陳述句,從而跳出回圈或者結束`switch`陳述句的執行。 ## 一、人物簡介 - 第一位閃亮登場,有請今后會一直教我們C語言的老師 —— 自在 ......

    uj5u.com 2023-06-07 07:42:25 more
  • C++面試八股文:static和const的關鍵字有哪些用法?

    某日二師兄參加XXX科技公司的C++工程師開發崗位第7面: > 面試官:C++中,`static`和`const`的關鍵字有哪些用法? > > 二師兄:`satic`關鍵字主要用在以下三個方面:1.用在全域作用域,修飾的變數或者函式為靜態的,限制在本檔案內使用。2.方法內修飾修飾靜態區域變數,在第一 ......

    uj5u.com 2023-06-07 07:42:19 more
  • 演算法 in Golang:Quicksort(快速排序)

    # 演算法 in Golang:Quicksort(快速排序) ## Quicksort(快速排序) - 快速排序 O(nlog2^n),比選擇排序要快 O(n²) - 在日常生活中經常使用 - 使用了 D & C 策略(分而治之) ## 使用 Quicksort 排序陣列 - 不需要排序的陣列(也就 ......

    uj5u.com 2023-06-07 07:42:15 more
  • Java中的金錢陷阱

    ### 前言 有多少小伙伴是被標題 ~~騙~~ 吸引進來的呢,我可不是標題黨,今天的文章呢確實跟”金錢“有關系。 但是我們說的不是過度追求金錢而掉入陷阱,而是要說一說在Java程式中,各種跟金錢運算有關的陷阱。 日常作業中我們經常會涉及到各種金額這樣**浮點數的運算**。 一旦涉及到金額的運算就必須 ......

    uj5u.com 2023-06-07 07:42:04 more
  • 【技識訓累】Python中的PyTorch庫【一】

    博客推行版本更新,成果積累制度,已經寫過的博客還會再次更新,不斷地琢磨,高質量高數量都是要追求的,工匠精神是學習必不可少的精神。因此,大家有何建議歡迎在評論區踴躍發言,你們的支持是我最大的動力,你們敢投,我就敢肝 ......

    uj5u.com 2023-06-07 07:41:56 more
  • 8-地圖可視化與seaborn可視化

    # 地圖可視化 basemap安裝和使用 區域縮放與繪圖 綜合案例:美國人口分布圖 # seaborn可視化 樣式與分布圖 分類圖 # basemap地圖繪制 地圖繪制也是資料可視化的一部分,常用的地圖繪制庫為basemap工具包,其為matplotlib的子包。本章將講解如何利用whl檔案在Pyt ......

    uj5u.com 2023-06-07 07:40:38 more
  • 演算法 in Golang:Recursion(遞回)

    # 演算法 in Golang:Recursion(遞回) ## 遞回演算法 ### 場景:在套娃中找到寶石 ### 可以這樣做 - while 沒找到: - if 當前項 is 寶石: - return 寶石 - else if 當前項 is 套娃: - 打開這個套娃 - if 當前項 is 寶石: ......

    uj5u.com 2023-06-07 07:39:52 more
  • JVM運行時資料區

    # JVM運行時資料區 ## 簡介 JVM運行時資料區包括:JVM堆疊(虛擬機堆疊),堆,方法區,本地方法堆疊,PC暫存器。大概的劃分就是堆疊和堆,以及一些其他的結構。重點在JVM堆疊,堆,方法區。JVM規范指出:方法區在邏輯上屬于堆,但是實際的具體的JVM中并不屬于堆的一部分。 在JVM堆疊中會發生GC和Er ......

    uj5u.com 2023-06-07 07:39:32 more