【图片识别Excel】批量提取图片中的文字,图片设置识别区域,识别后将文字提取并保存Excel表格,基于WPF和OCR识别的应用

news2025/6/8 13:52:32

应用场景 

在办公自动化、文档处理、数据录入等场景中,经常需要从大量图片中提取文字信息。例如:

  • 批量处理扫描的表单、合同、发票等文档
  • 从图片集中提取特定区域的文字数据
  • 将纸质资料快速转换为电子文本并整理归档

通过设置识别区域,可以精准定位需要提取的内容,提高识别准确率,减少干扰信息。

界面设计

应用界面采用 WPF 实现,主要包含以下部分:

  1. 顶部导航栏:包含应用标题和功能按钮(选择图片、开始识别、保存结果)
  2. 左侧图片预览区:显示当前选中的图片及识别区域框
  3. 中间参数设置区:可调整识别区域坐标和大小
  4. 右侧结果预览区:展示识别出的文字内容
  5. 底部状态栏:显示处理进度和状态信息

详细代码步骤

下面是基于 WPF 和 Tesseract OCR 的实现代码:

using System.Windows;

namespace OCRTextExtractor
{
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            
            // 确保Tesseract数据目录存在
            if (!System.IO.Directory.Exists("./tessdata"))
            {
                System.IO.Directory.CreateDirectory("./tessdata");
                MessageBox.Show("请将Tesseract OCR语言数据文件(.traineddata)放到应用程序目录下的tessdata文件夹中。", "缺少OCR数据", 
                    MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }
    }
}    
<Window x:Class="OCRTextExtractor.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="图片文字识别提取工具" Height="700" Width="1000"
        WindowStartupLocation="CenterScreen">
    <Grid>
        <!-- 顶部导航栏 -->
        <DockPanel Height="50" DockPanel.Dock="Top" Background="#333333">
            <Button Content="选择图片" Margin="10,5" Width="80" Command="{Binding SelectImagesCommand}" DockPanel.Dock="Left"/>
            <Button Content="开始识别" Margin="10,5" Width="80" Command="{Binding StartRecognitionCommand}" DockPanel.Dock="Left"/>
            <Button Content="保存结果" Margin="10,5" Width="80" Command="{Binding SaveResultsCommand}" DockPanel.Dock="Left"/>
            <Label Content="{Binding StatusMessage}" Foreground="White" VerticalContentAlignment="Center" Margin="10,0"/>
        </DockPanel>
        
        <!-- 主体内容区 -->
        <Grid>
            <!-- 左侧图片预览区 -->
            <GridSplitter Width="5" HorizontalAlignment="Left" Grid.Column="1" Background="#CCCCCC"/>
            <Grid Grid.Column="0" Margin="10">
                <Label Content="图片预览" FontWeight="Bold" Margin="0,0,0,5"/>
                <Border BorderBrush="Gray" BorderThickness="1" Margin="0,25,0,0">
                    <Canvas x:Name="ImageCanvas" Background="LightGray" MouseLeftButtonDown="Canvas_MouseLeftButtonDown" 
                            MouseMove="Canvas_MouseMove" MouseLeftButtonUp="Canvas_MouseLeftButtonUp">
                        <Image x:Name="PreviewImage" Stretch="Uniform"/>
                        <Rectangle x:Name="SelectionRect" Stroke="Red" StrokeThickness="2" Visibility="Hidden"/>
                    </Canvas>
                </Border>
                
                <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
                    <Button Content="上一张" Width="70" Command="{Binding PreviousImageCommand}" Margin="0,0,5,0"/>
                    <Button Content="下一张" Width="70" Command="{Binding NextImageCommand}"/>
                    <Label Content="{Binding CurrentImageInfo}" VerticalContentAlignment="Center" Margin="10,0,0,0"/>
                </StackPanel>
            </Grid>
            
            <!-- 中间参数设置区 -->
            <GridSplitter Width="5" HorizontalAlignment="Left" Grid.Column="3" Background="#CCCCCC"/>
            <Grid Grid.Column="2" Margin="10">
                <Label Content="识别区域设置" FontWeight="Bold" Margin="0,0,0,5"/>
                <StackPanel Orientation="Vertical" Margin="0,25,0,0">
                    <GroupBox Header="区域坐标" Margin="0,0,0,10">
                        <Grid Margin="5">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            
                            <Label Content="X:" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"/>
                            <TextBox Text="{Binding RecognitionRegion.X, Mode=TwoWay}" Grid.Row="0" Grid.Column="1" Margin="5,0"/>
                            <Label Content="Y:" Grid.Row="0" Grid.Column="2" VerticalAlignment="Center"/>
                            <TextBox Text="{Binding RecognitionRegion.Y, Mode=TwoWay}" Grid.Row="0" Grid.Column="3" Margin="5,0"/>
                            
                            <Label Content="宽度:" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"/>
                            <TextBox Text="{Binding RecognitionRegion.Width, Mode=TwoWay}" Grid.Row="1" Grid.Column="1" Margin="5,0"/>
                            <Label Content="高度:" Grid.Row="1" Grid.Column="2" VerticalAlignment="Center"/>
                            <TextBox Text="{Binding RecognitionRegion.Height, Mode=TwoWay}" Grid.Row="1" Grid.Column="3" Margin="5,0"/>
                        </Grid>
                    </GroupBox>
                    
                    <GroupBox Header="OCR设置" Margin="0,0,0,10">
                        <Grid Margin="5">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            
                            <Label Content="识别语言:" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"/>
                            <ComboBox ItemsSource="{Binding AvailableLanguages}" SelectedItem="{Binding SelectedLanguage}" 
                                      Grid.Row="0" Grid.Column="1" Margin="5,0"/>
                            
                            <CheckBox Content="自动保存每张识别结果" IsChecked="{Binding AutoSaveEachResult}" 
                                      Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="5,0"/>
                        </Grid>
                    </GroupBox>
                    
                    <Button Content="应用设置" Command="{Binding ApplySettingsCommand}" Height="30"/>
                </StackPanel>
            </Grid>
            
            <!-- 右侧结果预览区 -->
            <Grid Grid.Column="4" Margin="10">
                <Label Content="识别结果" FontWeight="Bold" Margin="0,0,0,5"/>
                <Border BorderBrush="Gray" BorderThickness="1" Margin="0,25,0,0">
                    <TextBox Text="{Binding RecognizedText, Mode=TwoWay}" AcceptsReturn="True" TextWrapping="Wrap" 
                             VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" IsReadOnly="True"/>
                </Border>
                
                <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
                    <Label Content="处理进度:" VerticalContentAlignment="Center"/>
                    <ProgressBar Value="{Binding ProgressValue}" Maximum="{Binding ProgressMaximum}" Width="200" Margin="5,0"/>
                    <Label Content="{Binding ProgressText}" VerticalContentAlignment="Center" Margin="5,0"/>
                </StackPanel>
            </Grid>
        </Grid>
    </Grid>
</Window>    
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using Prism.Commands;
using Prism.Mvvm;
using Tesseract;

namespace OCRTextExtractor
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainViewModel();
        }
        
        private Point? startPoint = null;
        
        private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            startPoint = e.GetPosition(ImageCanvas);
            SelectionRect.Visibility = Visibility.Visible;
            ImageCanvas.CaptureMouse();
        }
        
        private void Canvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (startPoint.HasValue && e.LeftButton == MouseButtonState.Pressed)
            {
                var endPoint = e.GetPosition(ImageCanvas);
                double x = Math.Min(startPoint.Value.X, endPoint.X);
                double y = Math.Min(startPoint.Value.Y, endPoint.Y);
                double width = Math.Abs(endPoint.X - startPoint.Value.X);
                double height = Math.Abs(endPoint.Y - startPoint.Value.Y);
                
                SelectionRect.SetValue(Canvas.LeftProperty, x);
                SelectionRect.SetValue(Canvas.TopProperty, y);
                SelectionRect.Width = width;
                SelectionRect.Height = height;
                
                var viewModel = DataContext as MainViewModel;
                if (viewModel != null)
                {
                    viewModel.RecognitionRegion.X = x;
                    viewModel.RecognitionRegion.Y = y;
                    viewModel.RecognitionRegion.Width = width;
                    viewModel.RecognitionRegion.Height = height;
                }
            }
        }
        
        private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            startPoint = null;
            ImageCanvas.ReleaseMouseCapture();
        }
    }
    
    public class MainViewModel : BindableBase
    {
        private List<string> imageFiles = new List<string>();
        private int currentImageIndex = 0;
        private BitmapSource currentImage;
        private string recognizedText;
        private string statusMessage = "就绪";
        private Rect recognitionRegion = new Rect(100, 100, 300, 200);
        private double progressValue;
        private double progressMaximum;
        private string progressText = "0/0";
        private bool autoSaveEachResult = false;
        private string selectedLanguage = "chi_sim";
        private List<string> availableLanguages = new List<string> { "eng", "chi_sim", "jpn" };
        
        public BitmapSource CurrentImage
        {
            get { return currentImage; }
            set { SetProperty(ref currentImage, value); }
        }
        
        public string RecognizedText
        {
            get { return recognizedText; }
            set { SetProperty(ref recognizedText, value); }
        }
        
        public string StatusMessage
        {
            get { return statusMessage; }
            set { SetProperty(ref statusMessage, value); }
        }
        
        public Rect RecognitionRegion
        {
            get { return recognitionRegion; }
            set { SetProperty(ref recognitionRegion, value); }
        }
        
        public double ProgressValue
        {
            get { return progressValue; }
            set { SetProperty(ref progressValue, value); }
        }
        
        public double ProgressMaximum
        {
            get { return progressMaximum; }
            set { SetProperty(ref progressMaximum, value); }
        }
        
        public string ProgressText
        {
            get { return progressText; }
            set { SetProperty(ref progressText, value); }
        }
        
        public bool AutoSaveEachResult
        {
            get { return autoSaveEachResult; }
            set { SetProperty(ref autoSaveEachResult, value); }
        }
        
        public string SelectedLanguage
        {
            get { return selectedLanguage; }
            set { SetProperty(ref selectedLanguage, value); }
        }
        
        public List<string> AvailableLanguages
        {
            get { return availableLanguages; }
            set { SetProperty(ref availableLanguages, value); }
        }
        
        public string CurrentImageInfo
        {
            get
            {
                if (imageFiles.Count == 0)
                    return "未选择图片";
                
                return $"图片 {currentImageIndex + 1}/{imageFiles.Count}: {Path.GetFileName(imageFiles[currentImageIndex])}";
            }
        }
        
        public ICommand SelectImagesCommand { get; private set; }
        public ICommand PreviousImageCommand { get; private set; }
        public ICommand NextImageCommand { get; private set; }
        public ICommand StartRecognitionCommand { get; private set; }
        public ICommand SaveResultsCommand { get; private set; }
        public ICommand ApplySettingsCommand { get; private set; }
        
        public MainViewModel()
        {
            SelectImagesCommand = new DelegateCommand(SelectImages);
            PreviousImageCommand = new DelegateCommand(PreviousImage, CanNavigateImage).ObservesProperty(() => CurrentImageInfo);
            NextImageCommand = new DelegateCommand(NextImage, CanNavigateImage).ObservesProperty(() => CurrentImageInfo);
            StartRecognitionCommand = new DelegateCommand(StartRecognition, CanStartRecognition).ObservesProperty(() => CurrentImageInfo);
            SaveResultsCommand = new DelegateCommand(SaveResults, CanSaveResults).ObservesProperty(() => RecognizedText);
            ApplySettingsCommand = new DelegateCommand(ApplySettings);
        }
        
        private void SelectImages()
        {
            var openFileDialog = new OpenFileDialog
            {
                Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif|所有文件|*.*",
                Multiselect = true,
                Title = "选择图片文件"
            };
            
            if (openFileDialog.ShowDialog() == true)
            {
                imageFiles = openFileDialog.FileNames.ToList();
                currentImageIndex = 0;
                LoadCurrentImage();
                StatusMessage = $"已选择 {imageFiles.Count} 张图片";
            }
        }
        
        private void LoadCurrentImage()
        {
            if (imageFiles.Count > 0)
            {
                try
                {
                    var image = new BitmapImage(new Uri(imageFiles[currentImageIndex]));
                    CurrentImage = image;
                    RecognizedText = "";
                    RaisePropertyChanged(nameof(CurrentImageInfo));
                }
                catch (Exception ex)
                {
                    StatusMessage = $"加载图片时出错: {ex.Message}";
                }
            }
        }
        
        private bool CanNavigateImage()
        {
            return imageFiles.Count > 0;
        }
        
        private void PreviousImage()
        {
            if (currentImageIndex > 0)
            {
                currentImageIndex--;
                LoadCurrentImage();
            }
        }
        
        private void NextImage()
        {
            if (currentImageIndex < imageFiles.Count - 1)
            {
                currentImageIndex++;
                LoadCurrentImage();
            }
        }
        
        private bool CanStartRecognition()
        {
            return imageFiles.Count > 0;
        }
        
        private void StartRecognition()
        {
            try
            {
                StatusMessage = "开始识别...";
                ProgressMaximum = imageFiles.Count;
                ProgressValue = 0;
                
                var results = new List<(string FileName, string Text)>();
                
                foreach (var imageFile in imageFiles)
                {
                    var text = RecognizeTextFromImage(imageFile);
                    results.Add((Path.GetFileName(imageFile), text));
                    
                    if (AutoSaveEachResult)
                    {
                        SaveSingleResult(imageFile, text);
                    }
                    
                    ProgressValue++;
                    ProgressText = $"{ProgressValue}/{ProgressMaximum}";
                }
                
                // 合并所有识别结果
                RecognizedText = string.Join(Environment.NewLine + "------------------------" + Environment.NewLine, 
                    results.Select(r => $"文件名: {r.FileName}{Environment.NewLine}识别结果:{Environment.NewLine}{r.Text}"));
                
                StatusMessage = "识别完成";
            }
            catch (Exception ex)
            {
                StatusMessage = $"识别过程中出错: {ex.Message}";
            }
        }
        
        private string RecognizeTextFromImage(string imagePath)
        {
            try
            {
                using (var engine = new TesseractEngine(@"./tessdata", SelectedLanguage, EngineMode.Default))
                {
                    using (var img = Pix.LoadFromFile(imagePath))
                    {
                        // 截取识别区域
                        if (RecognitionRegion.Width > 0 && RecognitionRegion.Height > 0)
                        {
                            int x = (int)Math.Max(0, RecognitionRegion.X);
                            int y = (int)Math.Max(0, RecognitionRegion.Y);
                            int width = (int)Math.Min(img.Width - x, RecognitionRegion.Width);
                            int height = (int)Math.Min(img.Height - y, RecognitionRegion.Height);
                            
                            if (width > 0 && height > 0)
                            {
                                using (var region = img.Copy(x, y, width, height))
                                {
                                    using (var page = engine.Process(region))
                                    {
                                        return page.GetText();
                                    }
                                }
                            }
                        }
                        
                        // 如果区域无效,则处理全图
                        using (var page = engine.Process(img))
                        {
                            return page.GetText();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                StatusMessage = $"处理图片 {Path.GetFileName(imagePath)} 时出错: {ex.Message}";
                return $"[识别出错: {ex.Message}]";
            }
        }
        
        private void SaveSingleResult(string imagePath, string text)
        {
            try
            {
                string resultPath = Path.ChangeExtension(imagePath, ".txt");
                File.WriteAllText(resultPath, text);
            }
            catch { } // 忽略单个文件保存错误
        }
        
        private bool CanSaveResults()
        {
            return !string.IsNullOrEmpty(RecognizedText);
        }
        
        private void SaveResults()
        {
            var saveFileDialog = new SaveFileDialog
            {
                Filter = "文本文件|*.txt|Excel文件|*.xlsx|所有文件|*.*",
                Title = "保存识别结果",
                DefaultExt = "txt"
            };
            
            if (saveFileDialog.ShowDialog() == true)
            {
                try
                {
                    if (Path.GetExtension(saveFileDialog.FileName).ToLower() == ".xlsx")
                    {
                        SaveToExcel(saveFileDialog.FileName);
                    }
                    else
                    {
                        File.WriteAllText(saveFileDialog.FileName, RecognizedText);
                    }
                    
                    StatusMessage = $"结果已保存到 {saveFileDialog.FileName}";
                }
                catch (Exception ex)
                {
                    StatusMessage = $"保存结果时出错: {ex.Message}";
                }
            }
        }
        
        private void SaveToExcel(string filePath)
        {
            // 使用EPPlus库创建Excel文件
            using (var package = new OfficeOpenXml.ExcelPackage(new FileInfo(filePath)))
            {
                var worksheet = package.Workbook.Worksheets.Add("识别结果");
                
                // 分割多个图片的识别结果
                var results = RecognizedText.Split(new[] { "------------------------" }, StringSplitOptions.RemoveEmptyEntries);
                
                // 添加标题行
                worksheet.Cells[1, 1].Value = "图片文件名";
                worksheet.Cells[1, 2].Value = "识别文本";
                
                // 填充数据
                for (int i = 0; i < results.Length; i++)
                {
                    var lines = results[i].Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
                    if (lines.Length >= 2 && lines[0].StartsWith("文件名:"))
                    {
                        string fileName = lines[0].Substring(5).Trim();
                        string text = string.Join(Environment.NewLine, lines.Skip(1));
                        
                        worksheet.Cells[i + 2, 1].Value = fileName;
                        worksheet.Cells[i + 2, 2].Value = text;
                    }
                }
                
                // 自动调整列宽
                worksheet.Cells.AutoFitColumns();
                
                // 保存文件
                package.Save();
            }
        }
        
        private void ApplySettings()
        {
            StatusMessage = "已应用识别区域设置";
        }
    }
}    

总结优化

这个应用实现了从图片中批量提取文字并保存到 Excel 的功能,主要特点包括:

  1. 用户友好的界面:直观的图片预览和识别区域设置功能
  2. 灵活的识别区域调整:支持鼠标绘制和手动输入坐标两种方式
  3. 多语言支持:内置多种语言识别选项
  4. 批量处理能力:支持多张图片连续处理
  5. 结果导出:支持将识别结果保存为文本或 Excel 格式

优化建议

  1. 性能优化

    • 可以添加多线程处理,提高批量识别效率
    • 实现识别结果的缓存机制,避免重复处理
  2. 功能增强

    • 添加图片预处理功能(灰度化、二值化、降噪等)提高识别准确率
    • 支持识别结果的编辑和校对
    • 增加更多导出格式选项(如 CSV、PDF 等)
  3. 用户体验

    • 添加识别进度的详细信息和预估时间
    • 支持保存和加载识别区域配置
    • 提供快捷键支持,提高操作效率

使用时,需要在应用程序目录下创建tessdata文件夹,并放入相应的语言数据文件(.traineddata),可以从 Tesseract GitHub 仓库下载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2404203.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt/C++学习系列之Excel使用记录

Qt/C学习系列之Excel使用记录 前言The process was ended forcefully.解决方式断点查语句问题 总结 前言 在项目中解析条目达50多条&#xff0c;并且都需要将对应的结果进行显示。为了将结果显示的更加清晰&#xff0c;考虑采用QTableWidget进行表格设置&#xff0c;而在使用过…

跳转指令四维全解:从【call/jmp 】的时空法则到内存迷宫导航术

一、核心概念&#xff1a;代码世界的空间定位法则 在汇编世界里&#xff0c;我们可以把内存想象成一栋巨大的图书馆&#xff1a; CS&#xff08;代码段寄存器&#xff09; 楼层编号 IP&#xff08;指令指针&#xff09; 房间编号 当前执行位置 CS:IP&#xff08;如3楼201…

LabVIEW实时系统数据监控与本地存储

基于LabVIEW Real-Time 模块&#xff0c;面向工业自动化、嵌入式测控等场景&#xff0c;提供实时数据采集、监控与本地存储的完整实现路径。通过分层任务调度、TDMS 文件格式应用及跨平台兼容性设计&#xff0c;确保系统在实时性、可靠性与数据管理效率间达到平衡。文中以 Comp…

从 Revit 到 3DTiles:GISBox RVT 切片器如何让建筑图元在 Web 端展示

在GIS&#xff08;地理信息系统&#xff09;行业蓬勃发展的当下&#xff0c;数据处理与展示的效率和精准度成为关键。GISBox作为一款功能强大的一站式三维GIS数据编辑、转换、发布平台&#xff0c;凭借其独特的“RVT切片器”功能&#xff0c;在RVT图元处理方面也有着不俗的表现…

Appium+python自动化(十二)- Android UIAutomator

Android团队在4.1版本&#xff08;API 16&#xff09;中推出了一款全新的UI自动化测试工具UiAutomator&#xff0c;用来帮助开发人员更有效率的完成App的Debug工作&#xff0c;同时对于测试人员也是一大福音&#xff0c;为什么这么说呢&#xff1f; UiAutomator提供了以下两种…

QQ邮箱发送验证码(Springboot)

一、邮箱发送服务准备 在qq邮箱的设置中选择账号下开启服务。 开启时可能会有短信验证&#xff0c;开启后显示验证码之类的一串英文&#xff0c;复制保存起来&#xff0c;在配置文件中会使用到。 二、后端依赖及配置 依赖 在pom.yml文件中添加相关依赖&#xff0c;redis的…

【EF Core】 EF Core并发控制:乐观锁与悲观锁的应用

文章目录 前言一、并发的风险二、EF Core中的并发控制方式2.1 开放式并发&#xff08;乐观锁&#xff09;2.1.1 应用程序管理的属性并发令牌2.1.2 数据库生成的并发令牌 2.2 悲观锁 总结 前言 实际的生产环境中&#xff0c;我们经常能遇到数据库由多个应用程序同时使用。每个程…

Harmony核心:动态方法修补与.NET游戏Mod开发

一、Harmony的核心定位与设计哲学 Harmony是一个运行时动态方法修补库&#xff0c;专为修改已编译的.NET/Mono应用程序而设计&#xff0c;尤其适用于游戏Mod开发。其核心创新在于&#xff1a; 非破坏性修改&#xff1a;保留原始方法完整性&#xff0c;避免直接替换或覆盖。多…

【Java开发日记】说一说 SpringBoot 中 CommandLineRunner

目录 1、CommandLineRunner SpringBoot中CommandLineRunner的作用 简单例子 多个类实现CommandLineRunner接口执行顺序的保证 通过实现Ordered接口实现控制执行顺序 通过Order注解实现控制执行顺序 Order 作用 2、ApplicationRunner 3、传递参数 4、源码跟踪 run()方…

全面理解 Linux 内核性能问题:分类、实战与调优策略

在 Linux 系统&#xff08;特别是嵌入式或服务器环境&#xff09;中&#xff0c;性能问题往往错综复杂、表象多变。只有对常见性能问题进行系统归类、理解其症状与根源&#xff0c;才能有效定位和解决。本文将围绕八大类核心性能问题&#xff0c;结合实战示例&#xff0c;逐类分…

算法-多条件排序

1、数对排序的使用 pair<ll,ll> a[31];//cmp为比较规则 ll cmp(pair<ll,ll>a,pair<ll,ll>b){if(a.first!b.first)return a.first>b.first;else return a.second<b.second; }//按照比较规则进行排序 sort(a1,a31,cmp); 2、具体例题 输入样例&#xff1…

固定ip和非固定ip的区别是什么?如何固定ip地址

在互联网中&#xff0c;我们常会接触到固定IP和非固定IP的概念。它们究竟有何不同&#xff1f;如何固定IP地址&#xff1f;让我们一起来探究这个问题。 一、固定IP和非固定IP的区别是什么 固定IP&#xff08;静态IP&#xff09;和非固定IP&#xff08;动态IP&#xff09;是两种…

使用矩阵乘法+线段树解决区间历史和问题的一种通用解法

文章目录 前言P8868 [NOIP2022] 比赛CF1824DP9990/2020 ICPC EcFinal G 前言 一般解决普通的区间历史和&#xff0c;只需要定义辅助 c h s − t ⋅ a chs-t\cdot a chs−t⋅a&#xff0c; h s hs hs是历史和&#xff0c; a a a是区间和&#xff0c; t t t是时间戳&#xff0c…

如何从浏览器中导出网站证书

以导出 GitHub 证书为例&#xff0c;点击 小锁 点击 导出 注意&#xff1a;这里需要根据你想要证书格式手动加上后缀名&#xff0c;我的是加 .crt 双击文件打开

低功耗MQTT物联网架构Java实现揭秘

文章目录 一、引言二、相关技术概述2.1 物联网概述2.2 MQTT协议java三、基于MQTT的Iot物联网架构设计3.1 架构总体设计3.2 MQTT代理服务器选择3.3 物联网设备设计3.4 应用服务器设计四、基于MQTT的Iot物联网架构的Java实现4.1 开发环境搭建4.2 MQTT客户端实现4.3 应用服务器实现…

ideal2022.3.1版本编译项目报java: OutOfMemoryError: insufficient memory

最近换了新电脑&#xff0c;用新电脑拉项目配置后&#xff0c;启动时报错&#xff0c;错误描述 idea 启动Springboot项目在编译阶段报错&#xff1a;java: OutOfMemoryError: insufficient memory 2. 处理方案 修改VM参数&#xff0c;分配更多内存 ❌ 刚刚开始以为时JVM内存设置…

centos7编译安装LNMP架构

一、LNMP概念 LNMP架构是一种常见的网站服务器架构&#xff0c;由Linux操作系统、Nginx Web服务器、MySQL数据库和PHP后端脚本语言组成。 1 用户请求&#xff1a;用户通过浏览器输入网址&#xff0c;请求发送到Nginx Web服务器。 2 Nginx处理&#xff1a;Nginx接收请求后&…

Spring Boot 3.3 + MyBatis 基础教程:从入门到实践

Spring Boot 3.3 MyBatis 基础教程&#xff1a;从入门到实践 在当今的Java开发领域&#xff0c;Spring Boot和MyBatis是构建高效、可维护的后端应用的两个强大工具。Spring Boot简化了Spring应用的初始搭建和开发过程&#xff0c;而MyBatis则提供了一种灵活的ORM&#xff08;…

征文投稿:如何写一份实用的技术文档?——以软件配置为例

&#x1f4dd; 征文投稿&#xff1a;如何写一份实用的技术文档&#xff1f;——以软件配置为例 目录 [TOC](目录)&#x1f9ed; 技术文档是通往成功的“说明书”&#x1f4a1; 一、明确目标读者&#xff1a;他们需要什么&#xff1f;&#x1f4cb; 二、结构清晰&#xff1a;让读…

tensorflow image_dataset_from_directory 训练数据集构建

以数据集 https://www.kaggle.com/datasets/vipoooool/new-plant-diseases-dataset 为例 目录结构 训练图像数据集要求&#xff1a; 主目录下包含多个子目录&#xff0c;每个子目录代表一个类别。每个子目录中存储属于该类别的图像文件。 例如 main_directory/ ...cat/ ...…