需求分析:
1、具体需求
本《读取Excel数据到客户表》有如下需求:
- 初始界面中可从本地电脑中选择Excel格式文件;
- 执行后,可将Excel中的数据写入到客户表(ZTCUSTOMER)中,同时显示写入日志。
2、开发分析
要达成本实践目标,主要通过功能函数,实现Excel文件的打开和数据的读取,分析有如下:
- 通过功能函数功能函数“WS_FILENAME_GET”可以选择文件,并在选择时可以过滤格式(本实践只读取Excel格式的文件);
- 功能函数“ALSM_EXCEL_TO_INTERNAL_TABLE”则可将指定Excel文件的指定范围(行列的开始结束)的数据读取到内表中,此内表由3个字段组成:ROW(行)、COL(列)、VALUE(值),即Excel数据读取到内表后,内表记录了每行每列的值,其结构与客户表是不同的;
- 因此在将Excel数据读取到内表后,还需对读取的内表数据遍历和写入到与客户表结构相似的内表中,然后才添加到客户表中,并根据执行的情况输出日志。
开发思路如图所示。
实践步骤:
本实践将会需要建立2个程序,一个是要在其他程序中包含的Include程序,另一个是输出列表数据的程序;2个程序都可以通过程序编辑器(SE38)即可完成,编写的代码将有如下几部分组成,按开发人员风格不同,其组成部分并非强制一致。
No | 部分 | 说明 |
---|---|---|
1 | 程序声明 | 声明本程序执行后是否包含标准标题,数据输出宽度和每页的行数量为多少 |
2 | 对象定义 | 定义要使用的结构等,以在程序执行过程中获得和输出数据 |
3 | 屏幕事件 | 调用子程序以能在界面中点击按钮选择Excel文件 |
4 | 获取和输出数据 | 调用通过子程序以实现Excel文件数据的获取和输出 |
5 | 子程序定义 | 定义子程序,并通过功能模块实现Excel文件的选择、文件数据的获取以及输出 |
1、程序声明
程序声明部分的代码如下:
REPORT zu0505_read_cust_from_excel NO STANDARD PAGE HEADING
LINE-SIZE 120 LINE-COUNT 80.
通过如上代码,设定了此程序输出不使用标准页眉,页面宽度120个字符,页面高度为80行。
2、对象定义
对象定义部分的代码如下:
******对象定义*****
TYPES:BEGIN OF cust_mode,
customerid TYPE ztcustomer-customerid,
customername TYPE ztcustomer-customername,
contact TYPE ztcustomer-contact,
cphone TYPE ztcustomer-cphone,
cfax TYPE ztcustomer-cfax,
country TYPE ztcustomer-country,
city TYPE ztcustomer-city,
region TYPE ztcustomer-region,
address TYPE ztcustomer-address,
zipcode TYPE ztcustomer-zipcode,
END OF cust_mode.
DATA: cust_stru TYPE cust_mode,
cust_itab TYPE TABLE OF cust_mode,
gt_excel TYPE TABLE OF alsmex_tabline WITH HEADER LINE,
ztcust_stru TYPE ztcustomer.
PARAMETERS: p_file TYPE rlgrap-filename MODIF ID m01.
如上代码,定义了一个类型cust_mode,并以此类型定义了一个结构cust_stru和一个内表cust_itab以获得和处理数据;并参考alsmex_tabline定义了一个含工作区的内表gt_excel以存储读取的Excel数据;同时参考了客户表ztcustomer定义两个一个结构ztcust_stru以将内表gt_excel的数据进行转换,以最终存储到ztcustomer表中。
最后,通过PARAMETERS,参考rlgrap-filename定义了变量p_file对应读取的Excel文件。
3、屏幕事件
屏幕事件部分的代码如下:
*&----------------------------------------------------------------------*
*& AT SELECTION-SCREEN 选择文件选择框时事件
*&----------------------------------------------------------------------*
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.
PERFORM get_filename.
通过AT SELECTION-SCREEN ON事件,定义了当p_file字段发生请求时,执行其后的代码块;而本实践是希望能选择Excel文件进行后续数据的读取,因此,通过调用子例程get_filename以选择Excel文件,此子例程的定义在随后的《5、子程序定义》部分实现。
4、获取和输出数据
获取和输出数据部分的代码如下:
*&----------------------------------------------------------------------*
*& START-OF-SELECTION
*&----------------------------------------------------------------------*
START-OF-SELECTION.
PERFORM read_excel.
PERFORM output_data.
通过START-OF-SELECTION事件,定义了在选择Excel文件并点击执行后的操作,其中分别通过调用子程序read_excel和output_data,读取Excel文件中的数据和将数据写入到客户表及输出日志。
5、子程序定义
本实践包含了3个子程序,包括get_filename(选择Excel文件)、read_excel(读取Excel文件数据)和output_data(数据写入客户表及输出日志)。
1)get_filename(选择Excel文件)
本子程序的代码如下:
*&---------------------------------------------------------------------*
*& FORM GET_FILENAME
*&---------------------------------------------------------------------*
* 获得文件名称
*----------------------------------------------------------------------*
FORM get_filename.
TRY.
CALL FUNCTION 'WS_FILENAME_GET'
EXPORTING
mask = ',Excel Files,*.xls*,CSV Files,*.csv,All files,*.*.'
mode = 'O'
IMPORTING
filename = p_file
EXCEPTIONS
inv_winsys = 01
no_batch = 02
selection_cancel = 03
selection_error = 04.
ENDTRY.
IF p_file EQ ''.
MESSAGE s050(zu03_mclass01) WITH '未选择文件!' DISPLAY LIKE 'E'.
ENDIF.
ENDFORM.
如上代码调用了功能模块“WS_FILENAME_GET”以选择Excel文件并将文件名赋值给p_file,以待后续读取和处理数据;其中通过参数“mask”可以根据后缀过滤选择的格式,如如上代码,Excel文件的后缀为xls,CSV文件的后缀为.csv。
2)read_excel(读取Excel文件数据)
本子程序的代码如下:
*&---------------------------------------------------------------------*
*& FORM READ_EXCEL
*&---------------------------------------------------------------------*
* 获得文件名称
*----------------------------------------------------------------------*
FORM read_excel.
TRY.
CALL FUNCTION 'ALSM_EXCEL_TO_INTERNAL_TABLE'
EXPORTING
filename = p_file
i_begin_col = 1
i_begin_row = 2
i_end_col = 100
i_end_row = 100
TABLES
intern = gt_excel
EXCEPTIONS
inconsistent_parameters = 1
upload_ole = 2
OTHERS = 3.
IF sy-subrc <> 0.
MESSAGE s050(zu03_mclass01) WITH '文件打开失败,请注意是否文件已在进程中打开!'
DISPLAY LIKE 'E'.
LEAVE LIST-PROCESSING.
ENDIF.
ENDTRY.
SORT gt_excel BY row col.
FIELD-SYMBOLS .
FIELD-SYMBOLS LIKE gt_excel.
LOOP AT gt_excel ASSIGNING .
"将cust_stru的第col个字段分配给符号
ASSIGN COMPONENT -col OF STRUCTURE cust_stru TO .
= -value. "对结构的字段进行赋值
AT END OF row.
APPEND cust_stru TO cust_itab.
CLEAR cust_stru.
ENDAT.
ENDLOOP.
ENDFORM.
通过如上代码,定义了一个名为read_excel的子程序,此子程序首先通过调用功能模块“ALSM_EXCEL_TO_INTERNAL_TABLE”,根据屏幕事件中获得的文件名称(p_file),按指定行列范围,读取数据并存储于gt_excel中。
由于内表gt_excel的结构跟ztcustomer表的结构不同,不能直接append给ztcustomer表,因此在读取数据到gt_excel后,先将数据写入结构cust_stru中。而写入数据的方法可有不同,此处则通过ASSIGN COMPONENT 语句,将结构的各个字段分配给符号,随后对符号进行赋值,以指定结构的各个字段值;另外也可通过for语句,遍历gt_excel每行(row)的数据,按列(col)顺序将值(value)赋给cust_stru的各个字段。
每行数据读取到cust_stru后,是可以直接将数据插入(INSERT INTO)到客户表ztcustomer中的,而本实践中先append到内表cust_itab中,随后再遍历插入数据到客户表及输出日志。
3)output_data(数据写入客户表及输出日志)
本子程序的代码如下:
*&---------------------------------------------------------------------*
*& FORM OUTPUT_DATA
*&---------------------------------------------------------------------*
* 输出数据
*----------------------------------------------------------------------*
FORM output_data.
WRITE:/5 '读取数据和导入日志如下:',
/5(110) sy-uline,
/5(3) '序号', (8) '客户编号', (15) '客户名称',
(8) '联系人',(10) '电话', (10) '传真',
(6) '传真', (6) '城市', (6) '区域',
(10) '地址', (6) '邮编', (6) '日志',
/5(110) sy-uline.
LOOP AT cust_itab INTO cust_stru.
WRITE:/5(3) sy-tabix, (8) cust_stru-customerid, (15) cust_stru-customername,
(8) cust_stru-contact, (10) cust_stru-cphone, (10) cust_stru-cfax,
(6) cust_stru-country, (6) cust_stru-city, (6) cust_stru-region,
(10) cust_stru-address, (6) cust_stru-zipcode.
TRY.
MOVE-CORRESPONDING cust_stru TO ztcust_stru.
INSERT INTO ztcustomer VALUES ztcust_stru.
ENDTRY.
IF sy-subrc = 0.
WRITE:'已添加' INVERSE ON COLOR COL_POSITIVE.
ELSE.
WRITE:'已存在' INVERSE ON COLOR COL_GROUP.
ENDIF.
ENDLOOP.
ENDFORM.
通过如上代码,定义了一个名为output_data的子程序,在其中,首先通过WRITE语句输出标题信息;然后通过LOOP AT遍历cust_itab数据,输出各记录的内容,然后将数据插入到客户表中;最后根据执行的结果(成功则sy-subrc为0),输出日志。
将如上5部分代码合成一起,则可以实现本实践的需求。