HarmonyOS實現快遞APP自動識別地址

? 大家好,我是潘Sir,持續分享IT技術,幫你少走彎路。《鴻蒙應用開發從入門到項目實戰》系列文章持續更新中,歡迎關注!

隨著鴻蒙(HarmonyOS)生態發展,越來越多的APP需要進行鴻蒙適配。本文以快遞APP寄件中的收貨地址識別功能為例,探討HarmonyOS鴻蒙版本的開發和適配。

一、需求分析

1、應用場景

隨著互聯網的發展,網購已成為大家日常生活中不可或缺的一部分。網購涉及到發貨和收貨操作,為了簡化操作、提升APP的使用效率,各大APP都融入了AI能力。在AI賦能下,從傳統的文字輸入交互方式,逐步拓展到人臉識別、指紋識別、圖片識別、語言識別等方式,降低了APP使用門檻的同時極大提高了交互效率。

本文研究以支付寶APP里邊的“菜鳥裹裹”寄件場景為例,通過粘貼文字或者圖片識別進行收貨地址自動識別填充。在鴻蒙操作系統(HarmonyOS)上借助HarmonyOS SDK 提供的AI能力,開發鴻蒙原生應用APP功能,完成上述功能。

ps:相信大家都寄過快遞,如果沒操作過可以先了解一下。

2、實現效果

主頁拍照識別保存
請添加圖片描述請添加圖片描述請添加圖片描述請添加圖片描述

ps:由于編寫的提取規則中,名字為2-4個中文,因此上邊的名字“潘Sir”包含了1個英文,故未識別正確。如果改為2-4個漢字則可以正確識別。這就是傳統的用規則來匹配的弊端,更好的方法是使用AI大模型或NLP工具來提取地址信息。

讀者可以直接運行提供的代碼查看效果。由于使用了視覺服務,需要真機運行。

使用說明:

  1. 點擊圖片識別按鈕,拉起選擇圖片獲取方式的彈窗,選擇拍照方式,通過對要識別的文字進行拍照獲得要識別的圖片。也可以選擇相冊方式,在圖庫中直接選擇需要識別的圖片。
  2. 識別出圖片包含的文本信息后,會自動將文本內容填充到文本輸入框。
  3. 點擊`地址解析按鈕,會將文本框中的信息提取為結構化數據,顯示到按鈕下方的列表中。
  4. 點擊保存地址按鈕,提示保存成功,文本框舊的內容會自動清空。

3、技術分析

基于HarmonyOS SDK提供的基礎視覺服務(CoreVisionKit),使用@kit.CoreVisionKit提供的通用文字識別能力,通過拍照(CameraPicker)或者相冊(PhotoViewPicker)方式,將印刷品文字(如:收貨信息)轉化為圖像信息,再利用文字識別技術將圖像信息轉化為設備可以使用的文本字符,最后可以根據實際業務規則提取結構化數據。

二、界面制作

1、布局分析

主界面布局分析:

請添加圖片描述

彈窗界面布局分析:
請添加圖片描述

2、界面制作

開發環境說明:DevEco Studio5.0.4 Release、 HarmonyOS5.0.4(API 16)

通過DevEco Studio創建項目,項目名稱為:ExtractAddress,刪除Index.ets文件中默認的代碼。

2.1 制作主界面

為了便于代碼復用和程序擴展,將收貨人信息的界面上的每一行顯示的數據,抽取為一個對象,該對象類型為ConsigneeInfo類。在ets目錄下新建viewmodel目錄,新建DataModel.ets文件,內容如下:

// 收貨人信息界面視圖模型
@ObservedV2
export class ConsigneeInfo {label: ResourceStr;       //標簽名稱placeholder: ResourceStr; //提示語@Trace value: string;     //輸入值constructor(label: ResourceStr, placeholder: ResourceStr, value: string) {this.label = label;this.placeholder = placeholder;this.value = value;}
}

有了此類,在主界面上就只需要實例化3個對象,通過列表進行循環渲染即可,避免了重復臃腫的代碼。接下來制作主界面,Index.ets文件內容如下:

import { ConsigneeInfo} from '../viewmodel/DataModel';@Entry
@Component
struct Index {@State consigneeInfos: ConsigneeInfo[] = []; //收貨人信息界面視圖@State saveAvailable: boolean = false; //保存按鈕是否可用aboutToAppear(): void {this.consigneeInfos = [new ConsigneeInfo('收貨人', '收貨人姓名', ''),new ConsigneeInfo('電話', '收貨人電話', ''),new ConsigneeInfo('地址', '地址', ''),];}build() {RelativeContainer() {// 界面主體內容Column() {Text('新增收貨地址').id('title').width('100%').font({ size: 26, weight: 700 }).fontColor('#000000').opacity(0.9).height(64).align(Alignment.TopStart)Text('地址信息').width('100%').padding({ left: 12, right: 12 }).font({ size: 14, weight: 400 }).fontColor('#000000').opacity(0.6).lineHeight(19).margin({ bottom: 8 })// 識別或填寫區域Column() {TextArea({placeholder: '圖片識別的文本自動顯示到此處(也可手動復制文本到此處),將自動識別收貨信息。例:大美麗,182*******,四川省成都市天府新區某小區',}).height(100).margin({ bottom: 12 }).backgroundColor('#FFFFFF')Row({ space: 12 }) {Button() {Row({ space: 8 }) {Text() {SymbolSpan($r('sys.symbol.camera')).fontSize(26).fontColor(['#0A59F2'])}Text('圖片識別').fontSize(16).fontColor('#0A59F2')}}.height(40).layoutWeight(1).backgroundColor('#F1F3F5')Button('地址解析').height(40).layoutWeight(1)}.width('100%').padding({left: 16,right: 16,})}.backgroundColor('#FFFFFF').borderRadius(16).padding({top: 16,bottom: 16})// 收貨人信息Column() {List() {//列表渲染,避免重復代碼ForEach(this.consigneeInfos, (item: ConsigneeInfo) => {ListItem() {Row() {Text(item.label).fontSize(16).fontWeight(400).lineHeight(19).textAlign(TextAlign.Start).fontColor('#000000').opacity(0.9).layoutWeight(1)TextArea({ placeholder: item.placeholder, text: item.value }).type(item.label === '收貨人' ? TextAreaType.PHONE_NUMBER : TextAreaType.NORMAL).fontSize(16).fontWeight(500).lineHeight(21).padding(0).borderRadius(0).textAlign(TextAlign.End).fontColor('#000000').opacity(0.9).backgroundColor('#FFFFFF').heightAdaptivePolicy(TextHeightAdaptivePolicy.MIN_FONT_SIZE_FIRST).layoutWeight(2).onChange((value: string) => {item.value = value;//判斷保存按鈕是否可用(均填寫即可保存)if (this.consigneeInfos[0].value && this.consigneeInfos[1].value && this.consigneeInfos[2].value) {this.saveAvailable = true;} else {this.saveAvailable = false;}})}.width('100%').constraintSize({ minHeight: 48 }).justifyContent(FlexAlign.SpaceBetween)}}, (item: ConsigneeInfo, index: number) => JSON.stringify(item) + index)}.width('100%').height(LayoutPolicy.matchParent).scrollBar(BarState.Off).divider({ strokeWidth: 0.5, color: '#33000000' }).padding({left: 12,right: 12,top: 4,bottom: 4}).borderRadius(16).backgroundColor('#FFFFFF')}.borderRadius(16).margin({ top: 12 }).constraintSize({ minHeight: 150, maxHeight: '50%' }).backgroundColor('#FFFFFF')}// 保存按鈕if (this.saveAvailable) {// 可用狀態Button("保存地址", { stateEffect: true }).width('100%').alignRules({bottom: { anchor: '__container__', align: VerticalAlign.Bottom }})} else {// 不可用狀態Button("保存地址", { stateEffect: false }).width('100%').alignRules({bottom: { anchor: '__container__', align: VerticalAlign.Bottom }}).opacity(0.4).backgroundColor('#317AFF')}}.height('100%').width('100%').padding({left: 16,right: 16,top: 24,bottom: 24}).backgroundColor('#F1F3F5').alignRules({left: { anchor: '__container__', align: HorizontalAlign.Start }}).expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])}
}

該界面中通過RelativeContainer進行相對布局,保存按鈕通過相對布局定位到界面底部,主要內容區域使用Column布局。在aboutToAppear周期函數中,初始化收貨人列表數據,界面中通過List列表渲染完成顯示。至此,主界面的靜態效果就實現了。

但主界面代碼依然較多,可以考慮將List列表渲染部分抽取為單獨的組件,提取到單獨文件中。在ets目錄下新建components目錄,在該目錄下新建ConsigneeInfoItem.ets文件,將主界面列表渲染部分的內容拷貝進去并進改造。

ConsigneeInfoItem.ets文件內容:

import { ConsigneeInfo } from '../viewmodel/DataModel';@Builder
export function ConsigneeInfoItem(item: ConsigneeInfo, checkAvailable?: () => void) {Row() {Text(item.label).fontSize(16).fontWeight(400).lineHeight(19).textAlign(TextAlign.Start).fontColor('#000000').opacity(0.9).layoutWeight(1)TextArea({ placeholder: item.placeholder, text: item.value }).type(item.label === '收貨人' ? TextAreaType.PHONE_NUMBER : TextAreaType.NORMAL).fontSize(16).fontWeight(500).lineHeight(21).padding(0).borderRadius(0).textAlign(TextAlign.End).fontColor('#000000').opacity(0.9).backgroundColor('#FFFFFF').heightAdaptivePolicy(TextHeightAdaptivePolicy.MIN_FONT_SIZE_FIRST).layoutWeight(2).onChange((value: string) => {item.value = value;//判斷保存按鈕是否可用(均填寫即可保存)checkAvailable?.();})}.width('100%').constraintSize({ minHeight: 48 }).justifyContent(FlexAlign.SpaceBetween)}

Index.ets改造

import  {ConsigneeInfoItem} from '../components/ConsigneeInfoItem'
...
ListItem() {//抽取為組件ConsigneeInfoItem(item,() => {if (this.consigneeInfos[0].value && this.consigneeInfos[1].value && this.consigneeInfos[2].value) {this.saveAvailable = true;} else {this.saveAvailable = false;}})}
...

主界面效果實現完成

2.2 圖片識別彈窗

點擊“圖片識別”按鈕,彈出獲取圖片方式選擇框。接下來完成該界面制作。

為了彈出框管理更加方面,封裝彈窗口管理工具類PromptActionManager,在ets目錄新建utils目錄,新建PromptActionManager.ets文件,內容如下:

import { promptAction } from '@kit.ArkUI';/*** Dialog管理類*/
export class PromptActionManager {static ctx: UIContext;static contentNode: ComponentContent<Object>;static options: promptAction.BaseDialogOptions;static setCtx(ctx: UIContext) {PromptActionManager.ctx = ctx;}static setContentNode(contentNode: ComponentContent<Object>) {PromptActionManager.contentNode = contentNode;}static setOptions(options: promptAction.BaseDialogOptions) {PromptActionManager.options = options;}static openCustomDialog() {if (!PromptActionManager.contentNode) {return;}try {PromptActionManager.ctx.getPromptAction().openCustomDialog(PromptActionManager.contentNode,PromptActionManager.options)} catch (error) {}}static closeCustomDialog() {if (!PromptActionManager.contentNode) {return;}try {PromptActionManager.ctx.getPromptAction().closeCustomDialog(PromptActionManager.contentNode)} catch (error) {}}
}

不同的彈出界面可能需要不同的樣式控制,因此為彈出框的控制定義參數類型Params。在DataModel.ets文件中新加類Params,代碼如下:

...
export class Params {uiContext: UIContext;textAreaController: TextAreaController;     //識別信息的TextArealoadingController: CustomDialogController;  //識別過程中的加載提示框constructor(uiContext: UIContext, textAreaController: TextAreaController, loadingController: CustomDialogController) {this.uiContext = uiContext;this.textAreaController = textAreaController;this.loadingController = loadingController;}
}

接下來制作彈出框組件界面,在components目錄新建dialogBuilder.ets文件

import { Params } from '../viewmodel/DataModel'
import { PromptActionManager } from '../common/utils/PromptActionManager'@Builder
export function dialogBuilder(params: Params): void {Column() {Text('圖片識別').font({ size: 20, weight: 700 }).lineHeight(27).margin({ bottom: 16 })Text('選擇獲取圖片的方式').font({ size: 16, weight: 50 }).lineHeight(21).margin({ bottom: 8 })Column({ space: 8 }) {Button('拍照').width('100%').height(40)Button('相冊').width('100%').height(40).fontColor('#0A59F2').backgroundColor('#FFFFFF')Button('取消').width('100%').height(40).fontColor('#0A59F2').backgroundColor('#FFFFFF').onClick(() => {PromptActionManager.closeCustomDialog();})}}.size({ width: 'calc(100% - 32vp)', height: 235 }).borderRadius(32).backgroundColor('#FFFFFF').padding(16)
}

修改Index.ets文件,為“圖片識別”按鈕綁定事件,點擊時彈出自定義對話框。

...
import { PromptActionManager } from '../common/utils/PromptActionManager';
import { ComponentContent, LoadingDialog } from '@kit.ArkUI';
import { ConsigneeInfo,Params} from '../viewmodel/DataModel';
import { dialogBuilder } from '../components/dialogBuilder';...
private uiContext: UIContext = this.getUIContext();
private resultController: TextAreaController = new TextAreaController();
private loadingController: CustomDialogController = new CustomDialogController({builder: LoadingDialog({content: '圖片識別中'}),autoCancel: false});
private contentNode: ComponentContent<Object> =new ComponentContent(this.uiContext, wrapBuilder(dialogBuilder),new Params(this.uiContext, this.resultController, this.loadingController));
...//圖片識別按鈕
.onClick(() => {PromptActionManager.openCustomDialog();})

靜態界面制作完成。

三、功能實現

1、通用功能封裝

創建OCR識別管理類OCRManager,需要用到HarmonyOS SDK中的AI和媒體兩類Kit。在utils目錄下新建OCRManager.ets,封裝相關方法。

import { textRecognition } from '@kit.CoreVisionKit';
import { camera, cameraPicker } from '@kit.CameraKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { image } from '@kit.ImageKit';
import { fileIo as fs } from '@kit.CoreFileKit';export class OCRManager {static async recognizeByCamera(ctx: Context, loadingController: CustomDialogController): Promise<string> {// The configuration information of cameraPickerlet pickProfile: cameraPicker.PickerProfile = {cameraPosition: camera.CameraPosition.CAMERA_POSITION_UNSPECIFIED};try {let result: cameraPicker.PickerResult =await cameraPicker.pick(ctx, [cameraPicker.PickerMediaType.PHOTO], pickProfile);if (!result || !result.resultUri) {return '';}loadingController.open();return OCRManager.recognizeText(result.resultUri);} catch (error) {loadingController.close();return '';}}static async recognizeByAlbum(loadingController: CustomDialogController): Promise<string> {try {let photoPicker: photoAccessHelper.PhotoViewPicker = new photoAccessHelper.PhotoViewPicker();let photoResult: photoAccessHelper.PhotoSelectResult =await photoPicker.select({MIMEType: photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE,maxSelectNumber: 1,isPhotoTakingSupported: false});if (!photoResult || photoResult.photoUris.length === 0) {return '';}loadingController.open();return OCRManager.recognizeText(photoResult.photoUris[0]);} catch (error) {loadingController.close();return '';}}static async recognizeText(uri: string): Promise<string> {// Visual information to be recognized.// Currently, only the visual information of the PixelMap type in color data format RGBA_8888 is supported.let visionInfo: textRecognition.VisionInfo = { pixelMap: await OCRManager.getPixelMap(uri) };let result: textRecognition.TextRecognitionResult = await textRecognition.recognizeText(visionInfo);visionInfo.pixelMap.release();return result.value;}static async getPixelMap(uri: string): Promise<image.PixelMap> {// Convert image resources to PixelMaplet fileSource = await fs.open(uri, fs.OpenMode.READ_ONLY);let imgSource: image.ImageSource = image.createImageSource(fileSource.fd);let pixelMap: image.PixelMap = await imgSource.createPixelMap();fs.close(fileSource);imgSource.release();return pixelMap;}
}

2、拍照識別

彈出框界面,為“拍照”按鈕綁定事件

import { common } from '@kit.AbilityKit'
import { OCRManager } from '../common/utils/OCRManager'//拍照按鈕
.onClick(async () => {PromptActionManager.closeCustomDialog();let text: string =await OCRManager.recognizeByCamera(params.uiContext.getHostContext() as common.UIAbilityContext,params.loadingController);params.loadingController.close();if (text) {params.textAreaController.deleteText();params.textAreaController.addText(text);}})

主界面,修改TextArea,傳入controller并雙向綁定識別結果。

...
@State ocrResult: string = '';
...//TextArea
controller: this.resultController,
text: $$this.ocrResult

拍照識別功能實現。

3、相冊識別

彈出框界面,為“相冊”按鈕綁定事件

//相冊按鈕
.onClick(async () => {PromptActionManager.closeCustomDialog();let text: string =await OCRManager.recognizeByAlbum(params.loadingController);params.loadingController.close();if (text) {params.textAreaController.deleteText();params.textAreaController.addText(text);}})

相冊識別功能實現。

4、地址解析

主界面的“地址解析”按鈕,將通過圖片識別或手工輸入的地址信息,解析顯示到對應的輸入框中。

封裝地址解析類AddressParse,本案例使用正則表達式進行匹配。后續可以使用大模型或NLP工具進行解析。

在utils目錄下新建AddressParse.ets文件

import { ConsigneeInfo } from '../../viewmodel/DataModel';export class AddressParse {static nameBeforeRegex = /([\w\u4e00-\u9fa5]+[\s\,\,\。]+|[\s\,\,\。]*)([\u4e00-\u9fa5]{2,4})[\s\,\,\。]+/;static nameAfterRegex = /[\s\,\,\。]+([\u4e00-\u9fa5]{2,4})[\s\,\,\。]*/;static nameTagRegex = /(?:收貨人|收件人|姓名|聯系人)[::\s]*([\u4e00-\u9fa5]{2,4})/i;static namePlainRegex = /[\u4e00-\u9fa5]{2,4}/;static phoneRegex =/(1[3-9]\d[\s-]?\d{4}[\s-]?\d{4})|(\d{3,4}[\s-]?\d{7,8})|(\(\d{2,4}\)[\s-]?\d{4,8})|(\+\d{1,4}[\s-]?\d{5,15})/g;static phoneHyphenRegex = /[\(\)\s-]/g;static addressKeywords =['收貨地址', '收件地址', '配送地址', '所在地區', '位置','地址', '寄至', '寄往', '送至', '詳細地址'];static addressNoiseWords = ['收貨人', '收件人', '姓名', '聯系人', '電話', '手機', '聯系方式', ':', ':', ',', ','];static extractInfo(text: string, info: ConsigneeInfo[]): ConsigneeInfo[] {const baseText: string = text.replace(/\s+/g, ' ')const phoneResult: string = AddressParse.extractPhone(baseText);const nameResult: string = AddressParse.extractName(baseText, phoneResult);const addressResult: string = AddressParse.extractAddress(baseText, phoneResult, nameResult);info[0].value = nameResult;info[1].value = phoneResult.replace(AddressParse.phoneHyphenRegex, '');info[2].value = addressResult;return info;}static extractPhone(text: string): string {const phoneMatch: RegExpMatchArray | null = text.match(AddressParse.phoneRegex);return phoneMatch ? phoneMatch[0] : '';}static extractName(text: string, phone: string): string {let name = '';// Try to extract from the labelconst nameFromTag = text.match(AddressParse.nameTagRegex);if (nameFromTag) {name = nameFromTag[1];}// Try to extract before or after the phoneif (!name && phone) {const phoneIndex = text.indexOf(phone);const beforePhone = text.substring(0, phoneIndex);const nameBefore = beforePhone.match(AddressParse.nameBeforeRegex);if (nameBefore) {name = nameBefore[2];}if (!name) {const afterPhone = text.substring(phoneIndex + phone.length);const nameAfter = afterPhone.match(AddressParse.nameAfterRegex);if (nameAfter) {name = nameAfter[1];}}}// Try to extract 2-4 Chinese characters directlyif (!name) {const nameMatch = text.match(AddressParse.namePlainRegex);if (nameMatch) {name = nameMatch[0];}}return name;}static extractAddress(text: string, phone: string, name: string): string {for (const keyword of AddressParse.addressKeywords) {const keywordIndex = text.indexOf(keyword);if (keywordIndex !== -1) {const possibleAddress = text.substring(keywordIndex + keyword.length).trim();// Clean up the beginning punctuationconst cleanedAddress = possibleAddress.replace(/^[::,,。、\s]+/, '');if (cleanedAddress.length > 5) {return cleanedAddress;}}}// Try to remove name and phone numberlet cleanedText = text;if (name) {cleanedText = cleanedText.replace(name, '');}if (phone) {cleanedText = cleanedText.replace(phone, '');}// Remove common distracting wordsAddressParse.addressNoiseWords.forEach(word => {cleanedText = cleanedText.replace(word, '');});// Extract the longest text segment that may contain an addressconst segments = cleanedText.split(/[\s,,。;;]+/).filter(seg => seg.length > 4);if (segments.length > 0) {// The segment containing the address key is preferredconst addressSegments = segments.filter(seg =>seg.includes('省') || seg.includes('市') || seg.includes('區') ||seg.includes('縣') || seg.includes('路') || seg.includes('街') ||seg.includes('號') || seg.includes('棟') || seg.includes('單元'));if (addressSegments.length > 0) {return addressSegments.join(' ');}// Otherwise select the longest segmentreturn segments.reduce((longest, current) =>current.length > longest.length ? current : longest, '');}// Finally, return the entire textreturn cleanedText;}
}

在主文件中調用地址解析方法,修改Index.ets文件

import { AddressParse } from '../common/utils/AddressParse';...
//地址解析按鈕
.onClick(() => {if (!this.ocrResult || !this.ocrResult.trim()) {this.uiContext.getPromptAction().showToast({ message: $r('app.string.empty_toast') });return;}this.consigneeInfos = AddressParse.extractInfo(this.ocrResult, this.consigneeInfos);})

5、保存地址

為保存按鈕綁定事件,清空界面數據并提示保存結果。

修改Index.ets文件,封裝clearConsigneeInfos模擬保存操作后清空數據。

...// 保存地址,清空內容clearConsigneeInfos() {for (const item of this.consigneeInfos) {item.value = '';}this.ocrResult = '';}...
//保存地址按鈕
.onClick(() => {if (this.consigneeInfos[0].value && this.consigneeInfos[1].value && this.consigneeInfos[2].value) {this.uiContext.getPromptAction().showToast({ message: '保存成功' });this.clearConsigneeInfos();}})

至此,功能完成。

《鴻蒙應用開發從入門到項目實戰》系列文章持續更新中,歡迎關注!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/98536.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/98536.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/98536.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CUDA編程13 - 測量每個Block的執行時間

一:概述 GPU 程序性能不是靠 CPU 那樣的“順序執行”來衡量的,而是靠線程塊(block)和多處理器(SM)利用率。每個 block 在 GPU 的不同多處理器上執行,順序不確定。傳統的 kernel 總體計時(比如 cudaEvent 計時整個 kernel)只能知道總時間,無法分析哪個 block 慢,為什…

敏捷開發-Scrum(下)

Scrum 核心構成&#xff1a;團隊、事件與工件的協同價值體系 在 Scrum 框架中&#xff0c;“團隊、事件、工件” 并非孤立的模塊&#xff0c;而是相互咬合的有機整體&#xff1a;Scrum 團隊是價值交付的執行核心&#xff0c;Scrum 事件是節奏把控與反饋調整的機制載體&#xff…

LeetCode 單調棧 739. 每日溫度

739. 每日溫度給定一個整數數組 temperatures &#xff0c;表示每天的溫度&#xff0c;返回一個數組 answer &#xff0c;其中 answer[i] 是指對于第 i 天&#xff0c;下一個更高溫度出現在幾天后。如果氣溫在這之后都不會升高&#xff0c;請在該位置用 0 來代替。 示例 1: 輸入…

Java-面試八股文-JVM篇

JVM篇 一.在JVM中&#xff0c;什么是程序計數器? 在 JVM&#xff08;Java Virtual Machine&#xff09; 中&#xff0c;程序計數器&#xff08;Program Counter Register&#xff0c;簡稱 PC 寄存器&#xff09; 是一塊較小的內存空間&#xff0c;用于記錄 當前線程所執行的字…

微算法科技(NASDAQ: MLGO)采用量子相位估計(QPE)方法,增強量子神經網絡訓練

隨著量子計算技術的迅猛發展&#xff0c;傳統計算機在處理復雜問題時所遇到的算力瓶頸日益凸顯。量子計算以其獨特的并行計算能力和指數級增長的計算潛力&#xff0c;為解決這些問題提供了新的途徑。微算法科技&#xff08;NASDAQ: MLGO&#xff09;探索量子技術在各種應用場景…

MySQL 備份的方法和最佳實踐

MySQL 是一種流行的開源關系數據庫管理系統&#xff0c;用于在線應用程序和數據倉庫。它以可靠性、有效性和簡單性而聞名。然而&#xff0c;與任何計算機系統一樣&#xff0c;由于硬件故障、軟件缺陷或其他不可預見的情況&#xff0c;存在數據丟失的可能性。因此&#xff0c;保…

應用層自定義協議、序列化和反序列化

1.自定義協議開發者根據特定應用場景的需要&#xff0c;自行設計和制定的通信規則和數據格式 1.1 核心組成部分一個典型的自定義協議通常包含以下幾個關鍵部分&#xff1a;?幀/報文格式 (Frame/Packet Format)??&#xff1a;定義了數據是如何打包的。這通常包括&#xff1a…

Excel VBA 中可用的工作表函數

Visual Basic for Applications (VBA) 中可用的工作表函數。可以在 VBA 中通過 Application.WorksheetFunction 對象調用。 下面我將按照字母分組&#xff0c;對每個函數進行簡要解釋&#xff0c;并給出在 VBA 中使用的示例。A 組Acos: 返回數字的反余弦值。 result Applicati…

OpenWrt + Docker 完整部署方案:CFnat + Cloudflared 一體化集成

AI生成&#xff08;可能是AI幻覺&#xff09; 項目架構概述 基于您現有的網絡配置&#xff08;IP: 192.168.1.1&#xff09;&#xff0c;本方案將CFnat服務作為網絡優化層整合到現有的Cloudflare隧道架構中&#xff0c;實現完整的網絡加速解決方案。 優化后的流量路徑 用戶訪問…

蒼穹外賣項目實戰(day7-1)-緩存菜品和緩存套餐功能-記錄實戰教程、問題的解決方法以及完整代碼

完整資料下載 通過網盤分享的文件&#xff1a;蒼穹外賣 鏈接: https://pan.baidu.com/s/1JJaFOodXOF_lNJSUiZ6qtw?pwdps2t 提取碼: ps2t 目錄 1、緩存菜品 &#xff08;1&#xff09;問題說明 &#xff08;2&#xff09;使用redis緩存部分數據 1-2、代碼完善 &#xff…

計算機畢業設計 基于Python+Django的醫療數據分析系統

精彩專欄推薦訂閱&#xff1a;在 下方專欄&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f496;&#x1f525;作者主頁&#xff1a;計算機畢設木哥&#x1f525; &#x1f496; 文章目錄 一、項目介紹二…

使用 chromedp 高效爬取 Bing 搜索結果

在數據采集領域&#xff0c;搜索引擎結果是重要的信息來源。但傳統爬蟲面對現代瀏覽器渲染的頁面時&#xff0c;常因 JavaScript 動態加載、跳轉鏈接加密等問題束手無策。本文將詳細介紹如何使用 Go 語言的chromedp庫&#xff0c;模擬真實瀏覽器行為爬取 Bing 搜索結果&#xf…

遺漏的需求

“編寫執行者的目的&#xff0c;僅用別名來表達需要傳遞的數據”&#xff0c;就如客戶信息用名字和地址表示一樣&#xff0c;這是一個很好的建議。然而&#xff0c;對程序員來說&#xff0c;這沒有提供軟件開發所必需的詳細信息。程序設計人員和用戶界面設計者需要準確地知道地…

《云原生故障診療指南:從假活到配置漂移的根治方案》

當云原生架構成為企業數字化轉型的標配,系統故障的形態也隨之發生了根本性變化。曾經那些“一目了然”的報錯信息逐漸消失,取而代之的是“指標正常卻服務不可用”“偶發故障無規律可循”等隱性問題。這些故障如同架構中的“暗物質”,看不見卻持續影響著系統的穩定性,其排查…

“從零到一:使用GitLab和Jenkins實現自動化CI/CD流水線”

GitLab倉庫 簡單的來說就是開發人員提交代碼的倉庫&#xff0c;用于團隊開發&#xff0c;GitLab 上托管的倉庫通常作為遠程倉庫使用&#xff0c;開發人員可以將本地的 Git 倉庫推送到 GitLab 上&#xff0c;也可以從 GitLab 克隆倉庫到本地進行開發。 Jenkins Jenkins 是一個開…

3D開發工具HOOPS助力造船業數字化轉型,打造更高效、更智能的船舶設計與協作!

造船業是一個高度復雜且競爭激烈的行業&#xff0c;涵蓋船體設計、結構分析、生產制造到運維管理的完整生命周期。面對龐大的CAD數據、多方協作的復雜流程以及數字化轉型的迫切需求&#xff0c;傳統工具往往顯得力不從心。 Tech Soft 3D的HOOPS SDK系列&#xff0c;正以其卓越…

Python調用MCP:無需重構,快速為現有應用注入AI與外部服務能力!

文章目錄 ?? 介紹 ?? ?? 演示環境 ?? ? MCP核心概念:AI世界的“USB-C” ? ??? MCP安裝與基礎使用 ??? ?? 安裝模塊 ?? 創建第一個MCP服務端 ?? Python中MCP客戶端的調用方案 ?? ?? 概述 ?? 深度解析 ?? 參數詳情 ?? 常用方法 ?? 不同傳輸協…

【鏈表】3.重排鏈表(medium)

重排鏈表&#xff08;medium&#xff09;題?描述&#xff1a;解法&#xff1a;算法思路&#xff1a;算法代碼&#xff1a;題?鏈接&#xff1a;143. 重排鏈表 題?描述&#xff1a; 給定?個單鏈表 L 的頭節點 head &#xff0c;單鏈表 L 表?為&#xff1a; L(0) → L(1) →…

蜜罐平臺-Hfish部署

Hfish簡介&#xff1a; HFish是一款社區型免費蜜罐&#xff0c;側重企業安全場景&#xff0c;從內網失陷檢測、外網威脅感知、威脅情報生產三個場景出發&#xff0c;為用戶提供可獨立操作且實用的功能&#xff0c;通過安全、敏捷、可靠的中低交互蜜罐增加用戶在失陷感知和威脅…

docker-容器

安裝docker yum install -y docker查看版本 docker version安裝docker-compose yum install -y docker-compose查看版本 docker-compose --version基礎鏡像構建 tar --exclude/var/lib -cvf euler.tar /etc /boot /var /tmp /usr /mnt /bin /sbin /lib /lib64將JDK等需要的中間…