• 重构的过程记录--之原始代码:


    代码:

      1 //This is c program code!
      2 /* *=+=+=+=+* *** *=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
      3   * 文档信息: *** :~/WORKM/stutyCode/linuxPrograming/manageDisk/diskManage.c
      4   * 版权声明: *** :(魎魍魅魑)MIT
      5   * 联络信箱: *** :guochaoxxl@163.com
      6   * 创建时间: *** :2020年12月12日的上午10:43
      7   * 文档用途: *** :数据结构与算法分析-c语言描述
      8   * 作者信息: *** :guochaoxxl(http://cnblogs.com/guochaoxxl)
      9   * 修订时间: *** :2020年第49周 12月12日 星期六 上午10:43 (第347天)
     10   * 文件描述: *** :自行添加
     11  * *+=+=+=+=* *** *+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+*/
     12  
     13 /*
     14    CD Database Application
     15 
     16    Beginning Linux Programming
     17 
     18    Version: Terminals
     19 
     20    Copyright (c) 1996,2007 Wrox Press
     21 
     22    This program is free software; you can redistribute it and/or modify
     23    it under the terms of the GNU General Public License as published by
     24    the Fee Software Foundation; either version 2 of the License, or (at
     25    your option) any later version.
     26 
     27    This program is distributed in the hopes that it will be useful, but
     28    WITHOUT ANY WARRANTY; without even the implied warranty of
     29    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
     30    General Public License for more details.
     31 
     32    You should have received a copy of the GNU General Public License
     33    along with this program; if not, write to the Free Software
     34    Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA.
     35 
     36  */
     37 
     38 /*
     39    Notes
     40 
     41    This version of the CD database application has been written
     42    using the information presented in the Terminals chapter.
     43 
     44    It is derived from the shell script presented in the Shell
     45    Programming chapter. It has not been redesigned for the C
     46    implementation, so many features of the shell original can
     47    still be seen in this version.
     48 
     49    There are some problems with this implementation that will
     50    be resolved in later revisions:
     51 
     52    It does not deal with commas in titles.
     53    It has a practical limit on tracks per CD to keep them on screen.
     54 
     55    The program deliberately uses the standard input and output
     56    file streams. It does not deal with re-directed input or
     57    output explicitly.  */
     58 
     59 #include <unistd.h>
     60 #include <stdlib.h>
     61 #include <stdio.h>
     62 #include <string.h>
     63 #include <curses.h>
     64 
     65 
     66 #define MAX_STRING (80)        /* Longest allowed response       */
     67 #define MAX_ENTRY (1024)    /* Longest allowed database entry */
     68 
     69 #define MESSAGE_LINE 6        /* Misc. messages go here         */
     70 #define ERROR_LINE   22        /* The line to use for errors     */
     71 #define Q_LINE       20        /* Line for Questions             */
     72 #define PROMPT_LINE  18        /* Line for prompting on          */
     73 
     74 /*
     75    The variable current_cd is used to store the CD title we are
     76    working with. It is initialized so that the first character is NUL
     77    to indicate 'no CD selected'. The  is strictly unnecessary, but
     78    serves to emphasize the point.
     79 
     80    The variable current_cat will be used to record the catalog number
     81    of the current CD.
     82  */
     83 
     84 static char current_cd[MAX_STRING] = "";
     85 static char current_cat[MAX_STRING];
     86 
     87 /*
     88    File names.
     89 
     90    These files are fixed in this version to keep things simple.
     91    The temporary file name is also fixed, this would cause a
     92    problem if this program is run by two users in the same directory.
     93 
     94    A better way to obtain data base file names would be either by
     95    program arguments, or from environment variables.
     96 
     97    We need a better way to generate a unique a temporary file name.
     98 
     99    Many of these issues will be addresed in later versions.
    100  */
    101 
    102 const char *title_file = "title.cdb";
    103 const char *tracks_file = "tracks.cdb";
    104 const char *temp_file = "cdb.tmp";
    105 
    106 
    107 /* Prototypes for local functions */
    108 void clear_all_screen(void);
    109 void get_return(void);
    110 int get_confirm(void);
    111 int getchoice(char *greet, char *choices[]);
    112 void draw_menu(char *options[], int highlight, int start_row, int start_col);
    113 void insert_title(char *cdtitle);
    114 void get_string(char *string);
    115 void add_record(void);
    116 void count_cds(void);
    117 void find_cd(void);
    118 void list_tracks(void);
    119 void remove_tracks(void);
    120 void remove_cd(void);
    121 void update_cd(void);
    122 
    123 
    124 /*
    125    Menu structures.
    126    The first character is the character to return when the
    127    chice is selected, the remaining text is to be displayed.
    128  */
    129 char *main_menu[] =
    130 {
    131     "aadd new CD",
    132     "ffind CD",
    133     "ccount CDs and tracks in the catalog",
    134     "qquit",
    135     0,
    136 };
    137 
    138 /*
    139    The extended menu is displayed when a CD is currently selected
    140  */
    141 char *extended_menu[] =
    142 {
    143     "aadd new CD",
    144     "ffind CD",
    145     "ccount CDs and tracks in the catalog",
    146     "llist tracks on current CD",
    147     "rremove current CD",
    148     "uupdate track information",
    149     "qquit",
    150     0,
    151 };
    152 
    153 int main()
    154 {
    155     int choice;
    156 
    157     initscr();
    158 
    159     do {
    160 
    161     choice = getchoice("Options:", current_cd[0] ? extended_menu : main_menu);
    162 
    163     switch (choice) {
    164     case 'q':
    165         break;
    166 
    167     case 'a':
    168         add_record();
    169         break;
    170 
    171     case 'c':
    172         count_cds();
    173         break;
    174 
    175     case 'f':
    176         find_cd();
    177         break;
    178 
    179     case 'l':
    180         list_tracks();
    181         break;
    182 
    183     case 'r':
    184         remove_cd();
    185         break;
    186 
    187     case 'u':
    188         update_cd();
    189         break;
    190     }
    191     } while (choice != 'q');
    192 
    193     endwin();
    194 
    195     return 0;
    196 }                /* main */
    197 
    198 /*
    199    getchoice - ask the user to choose
    200    passed: greet, an introduction
    201    choices, an array of strings, NULL at end
    202  */
    203 int getchoice(char *greet, char *choices[])
    204 {
    205     static int selected_row = 0;
    206     int max_row = 0;
    207     int start_screenrow = MESSAGE_LINE, start_screencol = 10;
    208     char **option;
    209     int selected;
    210     int key = 0;
    211 
    212     option = choices;
    213     while (*option) {
    214     max_row++;
    215     option++;
    216     }
    217 
    218     /* protect against menu getting shorted when CD deleted */
    219     if (selected_row >= max_row)
    220     selected_row = 0;
    221 
    222     clear_all_screen();
    223     mvprintw(start_screenrow - 2, start_screencol, greet);
    224 
    225     keypad(stdscr, TRUE);
    226     cbreak();
    227     noecho();
    228 
    229     key = 0;
    230     while (key != 'q' && key != KEY_ENTER && key != '
    ') {
    231     if (key == KEY_UP) {
    232         if (selected_row == 0)
    233         selected_row = max_row - 1;
    234         else
    235         selected_row--;
    236     }
    237     if (key == KEY_DOWN) {
    238         if (selected_row == (max_row - 1))
    239         selected_row = 0;
    240         else
    241         selected_row++;
    242     }
    243     selected = *choices[selected_row];
    244     draw_menu(choices, selected_row, start_screenrow, start_screencol);
    245     key = getch();
    246     }
    247 
    248     keypad(stdscr, FALSE);
    249     nocbreak();
    250     echo();
    251 
    252     if (key == 'q')
    253     selected = 'q';
    254 
    255     return (selected);
    256 
    257 }
    258 
    259 void draw_menu(char *options[], int current_highlight, int start_row, int start_col)
    260 {
    261     int current_row = 0;
    262     char **option_ptr;
    263     char *txt_ptr;
    264 
    265     option_ptr = options;
    266     while (*option_ptr) {
    267     if (current_row == current_highlight) attron(A_STANDOUT);
    268     txt_ptr = options[current_row];
    269     txt_ptr++;
    270     mvprintw(start_row + current_row, start_col, "%s", txt_ptr);
    271     if (current_row == current_highlight) attroff(A_STANDOUT);
    272     current_row++;
    273     option_ptr++;
    274     }
    275 
    276     mvprintw(start_row + current_row + 3, start_col, "Move highlight then press Return ");
    277 
    278     refresh();
    279 }
    280 
    281 /*
    282    clear_all_screen
    283 
    284    Clear the screen and re-write the title.
    285    If a CD is selected then display the information.
    286  */
    287 void clear_all_screen()
    288 {
    289     clear();
    290     mvprintw(2, Q_LINE, "%s", "CD Database Application");
    291     if (current_cd[0]) {
    292     mvprintw(ERROR_LINE, 0, "Current CD: %s: %s
    ", current_cat, current_cd);
    293     }
    294     refresh();
    295 }
    296 
    297 
    298 /*
    299    get_return
    300 
    301    Prompt for and read a carriage return.
    302    Ignore other characters.
    303  */
    304 void get_return()
    305 {
    306     int ch;
    307     mvprintw(23, 0, "%s", " Press return ");
    308     refresh();
    309     while ((ch = getchar()) != '
    ' && ch != EOF);
    310 }
    311 
    312 /*
    313    get_confirm
    314 
    315    Prompt for and read confirmation.
    316    Read a string and check first character for Y or y.
    317    On error or other character return no confirmation.
    318  */
    319 int get_confirm()
    320 {
    321     int confirmed = 0;
    322     char first_char = 'N';
    323 
    324     mvprintw(Q_LINE, 5, "Are you sure? ");
    325     clrtoeol();
    326     refresh();
    327 
    328     cbreak();
    329     first_char = getch();
    330     if (first_char == 'Y' || first_char == 'y') {
    331     confirmed = 1;
    332     }
    333     nocbreak();
    334 
    335     if (!confirmed) {
    336     mvprintw(Q_LINE, 1, "    Cancelled");
    337     clrtoeol();
    338     refresh();
    339     sleep(1);
    340     }
    341     return confirmed;
    342 }
    343 
    344 
    345 /*
    346    Database File Manipulation Functions
    347  */
    348 
    349 /*
    350    insert_title
    351 
    352    Add a title to the CD database
    353    Simply add the title string to the end of the titles file
    354 
    355  */
    356 void insert_title(char *cdtitle)
    357 {
    358     FILE *fp = fopen(title_file, "a");
    359     if (!fp) {
    360     mvprintw(ERROR_LINE, 0, "cannot open CD titles database");
    361     } else {
    362     fprintf(fp, "%s
    ", cdtitle);
    363     fclose(fp);
    364     }
    365 }
    366 
    367 
    368 /*
    369    get_string
    370 
    371    At the current screen position prompt for and read a string
    372    Delete any trailing newline.
    373  */
    374 void get_string(char *string)
    375 {
    376     int len;
    377 
    378     wgetnstr(stdscr, string, MAX_STRING);
    379     len = strlen(string);
    380     if (len > 0 && string[len - 1] == '
    ')
    381     string[len - 1] = '';
    382 }
    383 
    384 /*
    385    add_record
    386 
    387    Add a new CD to the collection
    388  */
    389 
    390 void add_record()
    391 {
    392     char catalog_number[MAX_STRING];
    393     char cd_title[MAX_STRING];
    394     char cd_type[MAX_STRING];
    395     char cd_artist[MAX_STRING];
    396     char cd_entry[MAX_STRING];
    397 
    398     int screenrow = MESSAGE_LINE;
    399     int screencol = 10;
    400 
    401     clear_all_screen();
    402     mvprintw(screenrow, screencol, "Enter new CD details");
    403     screenrow += 2;
    404 
    405     mvprintw(screenrow, screencol, "Catalog Number: ");
    406     get_string(catalog_number);
    407     screenrow++;
    408 
    409     mvprintw(screenrow, screencol, "      CD Title: ");
    410     get_string(cd_title);
    411     screenrow++;
    412 
    413     mvprintw(screenrow, screencol, "       CD Type: ");
    414     get_string(cd_type);
    415     screenrow++;
    416 
    417     mvprintw(screenrow, screencol, "        Artist: ");
    418     get_string(cd_artist);
    419     screenrow++;
    420 
    421     mvprintw(15, 5, "About to add this new entry:");
    422     sprintf(cd_entry, "%s,%s,%s,%s", catalog_number, cd_title, cd_type, cd_artist);
    423     mvprintw(17, 5, "%s", cd_entry);
    424     refresh();
    425 
    426     move(PROMPT_LINE, 0);
    427     if (get_confirm()) {
    428     insert_title(cd_entry);
    429     strcpy(current_cd, cd_title);
    430     strcpy(current_cat, catalog_number);
    431     }
    432 }
    433 
    434 /*
    435    count_cds - scan the database and count titles and tracks
    436  */
    437 void count_cds()
    438 {
    439     FILE *titles_fp, *tracks_fp;
    440     char entry[MAX_ENTRY];
    441     int titles = 0;
    442     int tracks = 0;
    443 
    444     titles_fp = fopen(title_file, "r");
    445     if (titles_fp) {
    446     while (fgets(entry, MAX_ENTRY, titles_fp))
    447         titles++;
    448     fclose(titles_fp);
    449     }
    450     tracks_fp = fopen(tracks_file, "r");
    451     if (tracks_fp) {
    452     while (fgets(entry, MAX_ENTRY, tracks_fp))
    453         tracks++;
    454     fclose(tracks_fp);
    455     }
    456     mvprintw(ERROR_LINE, 0, "Database contains %d titles, with a total of %d tracks.", titles, tracks);
    457     get_return();
    458 }
    459 
    460 /*
    461    find_cd - locate a CD in the database
    462 
    463    prompt for a substring to match in the database
    464    set current_cd to the CD title
    465 
    466  */
    467 void find_cd()
    468 {
    469     char match[MAX_STRING], entry[MAX_ENTRY];
    470     FILE *titles_fp;
    471     int count = 0;
    472     char *found, *title, *catalog;
    473 
    474     mvprintw(Q_LINE, 0, "Enter a string to search for in CD titles: ");
    475     get_string(match);
    476 
    477     titles_fp = fopen(title_file, "r");
    478     if (titles_fp) {
    479     while (fgets(entry, MAX_ENTRY, titles_fp)) {
    480 
    481         /* Skip past catalog number */
    482         catalog = entry;
    483         if (found = strstr(catalog, ",")) {
    484         *found = 0;
    485         title = found + 1;
    486 
    487         /* Zap the next comma in the entry to reduce it to title only */
    488         if (found = strstr(title, ",")) {
    489             *found = '';
    490 
    491             /* Now see if the match substring is present */
    492             if (found = strstr(title, match)) {
    493             count++;
    494             strcpy(current_cd, title);
    495             strcpy(current_cat, catalog);
    496             }
    497         }
    498         }
    499     }
    500     fclose(titles_fp);
    501     }
    502     if (count != 1) {
    503     if (count == 0)
    504         mvprintw(ERROR_LINE, 0, "Sorry, no matching CD found. ");
    505     if (count > 1)
    506         mvprintw(ERROR_LINE, 0, "Sorry, match is ambiguous: %d CDs found. ", count);
    507     current_cd[0] = '';
    508     get_return();
    509     }
    510 }
    511 
    512 
    513 /*
    514    remove_tracks - delete tracks from the current CD
    515  */
    516 void remove_tracks()
    517 {
    518     FILE *tracks_fp, *temp_fp;
    519     char entry[MAX_ENTRY + 1];
    520     int cat_length;
    521 
    522     if (current_cd[0] == '')
    523     return;
    524 
    525     cat_length = strlen(current_cat);
    526 
    527     tracks_fp = fopen(tracks_file, "r");
    528     if (tracks_fp == (FILE *)NULL) return;
    529     temp_fp = fopen(temp_file, "w");
    530 
    531     
    532 
    533     while (fgets(entry, MAX_ENTRY, tracks_fp)) {
    534     /* Compare catalog number and copy entry if no match */
    535     if (strncmp(current_cat, entry, cat_length) != 0)
    536         fputs(entry, temp_fp);
    537     }
    538     fclose(tracks_fp);
    539     fclose(temp_fp);
    540 
    541     unlink(tracks_file);
    542     rename(temp_file, tracks_file);
    543 }
    544 
    545 /*
    546    remove_cd - delete the current CD from the database
    547  */
    548 void remove_cd()
    549 {
    550     FILE *titles_fp, *temp_fp;
    551     char entry[MAX_ENTRY];
    552     int cat_length;
    553 
    554     if (current_cd[0] == '')
    555     return;
    556 
    557     clear_all_screen();
    558     mvprintw(PROMPT_LINE, 0, "About to remove CD %s: %s. ", current_cat, current_cd);
    559     if (!get_confirm())
    560     return;
    561 
    562     cat_length = strlen(current_cat);
    563 
    564     /* Copy the titles file to a temporary, ignoring this CD */
    565     titles_fp = fopen(title_file, "r");
    566     temp_fp = fopen(temp_file, "w");
    567 
    568     while (fgets(entry, MAX_ENTRY, titles_fp)) {
    569     /* Compare catalog number and copy entry if no match */
    570     if (strncmp(current_cat, entry, cat_length) != 0)
    571         fputs(entry, temp_fp);
    572     }
    573     fclose(titles_fp);
    574     fclose(temp_fp);
    575 
    576     /* Delete the titles file, and rename the temporary file */
    577     unlink(title_file);
    578     rename(temp_file, title_file);
    579 
    580     /* Now do the same for the tracks file */
    581     remove_tracks();
    582 
    583     /* Reset current CD to 'None' */
    584     current_cd[0] = '';
    585 }
    586 
    587 
    588 /*
    589    Some defines we use only for showing or entering the track information
    590  */
    591 #define BOXED_LINES    11
    592 #define BOXED_ROWS     60
    593 #define BOX_LINE_POS   8
    594 #define BOX_ROW_POS    2
    595 /*
    596    list_tracks - list the tracks for the current CD
    597  */
    598 void list_tracks()
    599 {
    600     FILE *tracks_fp;
    601     char entry[MAX_ENTRY];
    602     int cat_length;
    603     int lines_op = 0;
    604     WINDOW *track_pad_ptr;
    605     int tracks = 0;
    606     int key;
    607     int first_line = 0;
    608 
    609     if (current_cd[0] == '') {
    610     mvprintw(ERROR_LINE, 0, "You must select a CD first. ", stdout);
    611     get_return();
    612     return;
    613     }
    614     clear_all_screen();
    615     cat_length = strlen(current_cat);
    616 
    617     /* First count the number of tracks for the current CD */
    618     tracks_fp = fopen(tracks_file, "r");
    619     if (!tracks_fp)
    620     return;
    621     while (fgets(entry, MAX_ENTRY, tracks_fp)) {
    622     if (strncmp(current_cat, entry, cat_length) == 0)
    623         tracks++;
    624     }
    625     fclose(tracks_fp);
    626 
    627 
    628     /* Make a new pad, ensure that even if there is only a single
    629        track the PAD is large enough so the later prefresh() is always
    630        valid.
    631      */
    632     track_pad_ptr = newpad(tracks + 1 + BOXED_LINES, BOXED_ROWS + 1);
    633     if (!track_pad_ptr)
    634     return;
    635 
    636     tracks_fp = fopen(tracks_file, "r");
    637     if (!tracks_fp)
    638     return;
    639 
    640     mvprintw(4, 0, "CD Track Listing
    ");
    641 
    642     /* write the track information into the pad */
    643     while (fgets(entry, MAX_ENTRY, tracks_fp)) {
    644     /* Compare catalog number and output rest of entry */
    645     if (strncmp(current_cat, entry, cat_length) == 0) {
    646         mvwprintw(track_pad_ptr, lines_op++, 0, "%s", entry + cat_length + 1);
    647     }
    648     }
    649     fclose(tracks_fp);
    650 
    651     if (lines_op > BOXED_LINES) {
    652     mvprintw(MESSAGE_LINE, 0, "Cursor keys to scroll, RETURN or q to exit");
    653     } else {
    654     mvprintw(MESSAGE_LINE, 0, "RETURN or q to exit");
    655     }
    656     wrefresh(stdscr);
    657     keypad(stdscr, TRUE);
    658     cbreak();
    659     noecho();
    660 
    661     key = 0;
    662     while (key != 'q' && key != KEY_ENTER && key != '
    ') {
    663     if (key == KEY_UP) {
    664         if (first_line > 0)
    665         first_line--;
    666     }
    667     if (key == KEY_DOWN) {
    668         if (first_line + BOXED_LINES + 1 < tracks)
    669         first_line++;
    670     }
    671     /* now draw the appropriate part of the pad on the screen */
    672     prefresh(track_pad_ptr, first_line, 0,
    673          BOX_LINE_POS, BOX_ROW_POS,
    674          BOX_LINE_POS + BOXED_LINES, BOX_ROW_POS + BOXED_ROWS);
    675 /*    wrefresh(stdscr); */
    676     key = getch();
    677     }
    678 
    679     delwin(track_pad_ptr);
    680     keypad(stdscr, FALSE);
    681     nocbreak();
    682     echo();
    683 }
    684 
    685 /*
    686    update_cd - re-enter tracks for current CD
    687 
    688    deletes all tracks for the current CD in the database
    689    and then prompts for new ones.
    690  */
    691 void update_cd()
    692 {
    693     FILE *tracks_fp;
    694     char track_name[MAX_STRING];
    695     int len;
    696     int track = 1;
    697     int screen_line = 1;
    698     WINDOW *box_window_ptr;
    699     WINDOW *sub_window_ptr;
    700 
    701     clear_all_screen();
    702     mvprintw(PROMPT_LINE, 0, "Re-entering tracks for CD. ");
    703     if (!get_confirm())
    704     return;
    705     move(PROMPT_LINE, 0);
    706     clrtoeol();
    707 
    708     remove_tracks();
    709 
    710     mvprintw(MESSAGE_LINE, 0, "Enter a blank line to finish");
    711 
    712     tracks_fp = fopen(tracks_file, "a");
    713 
    714     /* Just to show how, enter the information in a scrolling, boxed,
    715        window. The trick is to set-up a sub-window, draw a box around the
    716        edge, then add a new, scrolling, sub-window just inside the boxed
    717        sub-window. */
    718     box_window_ptr = subwin(stdscr, BOXED_LINES + 2, BOXED_ROWS + 2,
    719                 BOX_LINE_POS - 1, BOX_ROW_POS - 1);
    720     if (!box_window_ptr)
    721     return;
    722     box(box_window_ptr, ACS_VLINE, ACS_HLINE);
    723 
    724     sub_window_ptr = subwin(stdscr, BOXED_LINES, BOXED_ROWS,
    725                 BOX_LINE_POS, BOX_ROW_POS);
    726     if (!sub_window_ptr)
    727     return;
    728     scrollok(sub_window_ptr, TRUE);
    729     werase(sub_window_ptr);
    730     touchwin(stdscr);
    731 
    732     do {
    733 
    734     mvwprintw(sub_window_ptr, screen_line++, BOX_ROW_POS + 2, "Track %d: ", track);
    735     clrtoeol();
    736     refresh();
    737     wgetnstr(sub_window_ptr, track_name, MAX_STRING);
    738     len = strlen(track_name);
    739     if (len > 0 && track_name[len - 1] == '
    ')
    740         track_name[len - 1] = '';
    741 
    742     if (*track_name)
    743         fprintf(tracks_fp, "%s,%d,%s
    ", current_cat, track, track_name);
    744     track++;
    745     if (screen_line > BOXED_LINES - 1) {
    746         /* time to start scrolling */
    747         scroll(sub_window_ptr);
    748         screen_line--;
    749     }
    750     } while (*track_name);
    751     delwin(sub_window_ptr);
    752 
    753     fclose(tracks_fp);
    754 }

    这个代码的缺点非常明显,就是把所有的功能硬塞进一个代码文件中,非常不方便调试。

    Makefile文件:

     1 #all: curses_app                                                                                                                               
     2 all: diskManage
     3 
     4 #Uncomment and edit the line below if necessary
     5 #CFLAGS=-I/usr/include/ncurses
     6 
     7 LDFLAGS=-lcurses

    不多说了。

  • 相关阅读:
    转发和重定向的区别
    关于Daydream VR的最直白的介绍
    Duplicate Protocol Definition of DTService Is Ignored
    automatically select architectures
    java
    初识反射
    java网络编程
    Map接口
    Set,List
    正则表达式
  • 原文地址:https://www.cnblogs.com/guochaoxxl/p/14139903.html
Copyright © 2020-2023  润新知