20160229.CCPP体系详解(0039天)

程序片段(01):大文本数据切割与合并.c
内容概要:文本文件切割与合并

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

#define ROWS 13180280

char * pSrcPath = "E:\\Resource\\TestData\\BigDB\\DangDang.txt";
char * pDesPath = "D:\\TestSpace\\TestData\\DangDangNew.txt";

//01.统计该文本文件所占据的总数据行数
//  1.这里是先判断硬盘文件还有没有数据
//  2.如果存在数据,则统计数据行的操作
//  3.这里只有使用了fgets();才能说明是按行
//      进行数据读取,才能准确的进行总行数的统计
//02.文件操作的顺序:
//  1.明确文件类型(文本文件+进制文件)
//  2.明确读写模式(只读模式+只写模式+读写模式+追加模式)
//  3.明确操作方式(单字节+多字节+行数据)
//  4.明确判断结尾(文本文件+通用文件)
int txtFileCountRows(char * pSrcPath)
{
    FILE * pfr = fopen(pSrcPath, "rt");
    if (NULL == pfr)
        abort();
    int rowNum = 0;
    while (!feof(pfr))
    {
        char str[256] = { 0 };
        fgets(str, 255, pfr);
        ++rowNum;
    }
    fclose(pfr);
    return rowNum;
}

//03.单个文本文件切割:
//  1.文件切割:单个文件-->多个文件
//  2.开辟一个指针数组,在对指针数组当中的每个指针开辟一段儿堆内存用于存储
//      分割路径字符串
//  3.字符数组就是用于存储文本文件的路径信息
//注:文件路径分割方式的效率问题分析
//  100/10=10..0(整除)
//  100/9=11...1(否整除)
//      100/(9-1) =12..4
//注:分割的文本文件路径最好恰好是CPU逻辑内核的整数倍,有助于提升CPU的数据处理
//  效率-->保证前(N-1)个文件数据一致,最后一个文件数据特殊-->有助于负载均衡
//注:区分文件的细致操作(字节读取or按行读取)
void divide(char * pSrcPath, int num)
{
    char ** pathEs = (char **)malloc(num * sizeof(char *));
    for (int i = 0; i < num; ++i)
    {
        *(pathEs + i) = (char *)malloc(256 * sizeof(char));
        sprintf(*(pathEs + i), "D:\\TestSpace\\TestData\\DangDang%02d.txt", i + 1);
        //printf("%s \n", *(pathEs + i));
    }
    FILE * pfr = fopen(pSrcPath, "rt");
    if (NULL == pfr)
        abort();
    int quotient = ROWS / num;
    int remainder = ROWS % num;
    if (!remainder)
    {
        for (int i = 0; i < num; ++i)//分文件
        {
            FILE * pfw = fopen(pathEs[i], "wt");
            for (int j = 0; j < quotient; ++j)//分行数
            {
                char str[1024] = { 0 };
                fgets(str, 1023, pfr);//执行多少次,就读取多少行
                fputs(str, pfw);
            }
            fclose(pfw);
        }
    }
    else
    {
        for (int i = 0; i < num - 1; ++i)//分文件
        {
            FILE * pfw = fopen(pathEs[i], "wt");
            for (int j = 0; j < ROWS / (num - 1); ++j)
            {
                char str[1024] = { 0 };
                fgets(str, 1023, pfr);
                fputs(str, pfw);
            }
            fclose(pfw);
        }
        FILE * pfw = fopen(pathEs[num - 1], "wt");
        for (int i = 0; i < ROWS % (num - 1); ++i)
        {
            char str[1024] = { 0 };
            fgets(str, 1023, pfr);
            fputs(str, pfw);
        }
    }
    fclose(pfr);
}

//04.多个文本文件合并:
//  切割过程与合并过程:
//      切割:一段儿一段儿的进行文本文件的顺序切割操作
//      合并:一段儿一段儿的进行文本文件的顺序合并操作
void merge(char * pSrcPath, int num)
{
    char ** pathEs = (char **)malloc(num * sizeof(char *));
    for (int i = 0; i < num; ++i)
    {
        *(pathEs + i) = (char *)malloc(256 * sizeof(char));
        sprintf(*(pathEs + i), "D:\\TestSpace\\TestData\\DangDang%02d.txt", i + 1);
    }
    FILE * pfw = fopen(pSrcPath, "wt");
    if (NULL == pfw)
        abort();
    for (int i = 0; i < num; ++i)//分文件
    {
        FILE * pfr = fopen(*(pathEs + i), "rt");
        while (!feof(pfr))
        {
            char str[1024] = { 0 };
            fgets(str, 1023, pfr);
            fputs(str, pfw);
        }
        fclose(pfr);
    }
    fclose(pfw);
}

//05.文件分割与合并流程:
//  1.首先获取行数,然后在决定切割与合并操作
//  2.现在就需要结合多线程技术在文件切割与合并操作
//  3.任务管理器:IO读取非常多,CPU消耗也非常多
//注:由于文本文件是按行进行切割的,因此每个切割之后的文件尺寸不一致,但是数据行数确实一样的
//  只有最后一个文件的行数可能不一样
int main(void)
{
    //printf("DangDang.rows = %d \n", txtFileCountRows(pSrcPath));

    //divide(pSrcPath, 23);

    merge("D:\\TestSpace\\TestData\\DangDang.txt", 23);

    system("pause");
}



//一.文本文件切割与合并:
//01.大数据相关数据:DangDang网未加密数据处理
//  1.进行分块儿数据处理[字节分&按行分(效率高)]
//  2.整体取余-->分块儿-->合并[文本切割器原理]
//02.两万行的C语言小项目:
//  1.模块儿[文本文件切割与合并]
//  2.配套功能:多线程加速检索功能
//03.文本文件切割与合并:
//  1.大文本数据切割与合并[普通]
//  2.昨天是二进制文件的切割与合并[简单]
//04.文本文件切割原理:
//  1.首先获取一个参数:行数-->文本路径
//  2.实现文本文件按行进行切割文件所需要的函数
//      fgets();&fputs();-->操作对象是-自定义缓冲区&数据行
//      (1)fgets();正常:数据行字符串首地址--失败:NULL
//      (2)fputs():正常:写入的最后一个字符--失败:EOF
//  3.多线程加速原理
//05.文件切割的好处:便于后期对数据的加工处理

程序片段(02):01.多线程检索内存.c+02.多线程多文件处理.c
内容概要:多线程处理数据

///01.多线程检索内存.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <process.h>    
#include <Windows.h>    

char *path = "D:\\TestData\\DB\\DangDang.txt";
#define N 13180820
char **g_pp;//1.二级指针,存储一级指针数组首地址,因为一级指针数组首地址作为函数参数的时候会转化为二级指针以作处理[存储的是字符数组首地址所组成的一级指针数组首地址]
struct threads *pthread;//2.完成多线程内存检索之后方便于释放操作[统一管理]

//01构建内存数据库模型
void init(char *path)
{//(1)初始化路径
    printf("init start!");
    g_pp = malloc(sizeof(char*)*N);//(2)开辟一个一级指针数组,然后一样一行的进行数据读取并加载进堆内存结构体,形成内存数据库模型
    memset(g_pp, '\0', sizeof(char*)*N);//(3)使用malloc();函数开辟的堆内存空间,可能存在垃圾地址,需要手动进行堆内存清理动作
    FILE *pfr = fopen(path, "r");
    if (pfr==NULL)
    {
        printf("init fail!!!");
    }
    else
    {
        for (int i = 0; i < N; i++)
        {
            char str[1024] = { 0 };//(4)缓冲区编程:采用自定义内存栈内存缓冲区进行数据临时汇集操作
            fgets(str, 1023, pfr);//读取
            int length = strlen(str);//长度
            if (length>=1)
            {//(5)有的时候会获取数据失败,这里是为了保证安全而进行的操作,可能存在空行等因素
                g_pp[i] = malloc(sizeof(char)*length + 1);//(6)+1是为了考虑字符串结尾'\0'的操作
                memset(g_pp[i], '\0', length + 1);//清空堆内存创建时期产生的垃圾内容
                if (g_pp[i]!=NULL)
                {//(7)填充堆内存内容,实现堆内存数据库模型单行字符串数据创建
                    strcpy(g_pp[i], str);
                }
            }
        }
        fclose(pfr);
    }
    printf("init end!");
}

//02执行内存数据库检索功能
void search(char *str)
{//(1)查找需要进行两步运算动作
    for (int i = 0; i < N; i++)
    {//(2)循环遍历内存数据库模型,字符串数据行
        if (g_pp[i]!=NULL)
        {
            char *p = strstr(g_pp[i], str);
            if (p!=NULL)
            {//(3)检索到指定数据
                printf("%s\n", g_pp[i]);
            }
        }
    }
}

//03单条线程处理内存数据库的信息[线程+处理信息]
struct threads
{//(1)为单条线程检索信息创建辅助信息结构体
    //(2)获取地址:内存数据库模型-->指针数组-->获取字符串地址,访问内存数据库当中的字符串
    //      指针数组的地址是一个二级指针-->指针数组在作为函数参数的时候回自动提升为二级指针
    char **ppstart;//1)指针数组的起始地址
    int length;//2)往前读取多少个元素[字符串个数]
    int id;//3)单条处理线程的编号
    //(3)在字符串数组里边儿查找一个字符串
    char *pstr;
};

//04单条线程处理内存数据库的操作
void searchthread(void *p)
{
    struct threads *pinfo = p;//(1)自动类型转换,明确的指针类型决定了该指针指向内容的解析方式,用于解析结构体
    for (int i = 0; i < pinfo->length; i++)
    {
        if (pinfo->ppstart[i]!=NULL)
        {//(2)数据安全检查,防止访问到无效数据,造成异常
            char *p = strstr(pinfo->ppstart[i], pinfo->pstr);//(3)对内存数据库模型当中的每一行字符串进行检索功能
            if (p!=NULL)
            {
                printf("线程%d找到%s字符串!\n", pinfo->id, pinfo->ppstart[i]);
            }
        }
    }
}

//05采用多线程进行数据检索动作
void searchwiththread(char *str)
{//(1)多线程检索大数据
    //(2)需要进行分段儿,切割为多个线程进行数据处理[处理动作,采用指针指向的方式为内存数据库进行切割分段儿让各个线程进行分块儿处理]
    //(3)多线程个数制定:一般情况下建议是CPU的逻辑内核倍数-->为了让CPU的多个逻辑内核实现负载均衡-->这里还是非理想情况之下的数据
    int num = 23;
    pthread = malloc(sizeof(struct threads) * 23);
    memset(pthread, '\0', sizeof(struct threads) * 23);
    //(4)为了管理线程的操作需要使用HANDLE
    HANDLE **phd = malloc(sizeof(HANDLE) * 23);
    if (N%num==0)
    {//(5)每一个线程分享一个数组信息
        //(6)开辟堆内存的原因是为了防止内存的自动回收释放,丢失threass
        //      销毁之后,线程访问函数的内容就会发生巨大变化,也就会出错,所以必须要在堆内存上面开辟
        for (int i = 0; i < num; i++)
        {
            pthread[i].id = i;
            pthread[i].pstr = str;
            pthread[i].length = N / num;//100/5=20个
            pthread[i].ppstart = g_pp + i*(N / num);//起始地址-->字符串个数的划分
            phd[i] = _beginthread(searchthread, 0, &pthread[i]);//创建线程,开始进行检索动作
        }
    }
    else
    {//100-->9份-->[8]100/(9-1)=12...4---[1]100%(9-1)=4-->前8线程个数:12线程处理的字符串个数+后1个线程:4处理的字符串个数
        for (int i = 0; i < num-1; i++)
        {
            pthread[i].id = i;
            pthread[i].pstr = str;
            pthread[i].length = N / (num - 1);
            pthread[i].ppstart = g_pp + i*(N / num - 1);
            phd[i] = _beginthread(searchthread, 0, &pthread[i]);//创建线程并执行
        }
        {
            int i = num - 1;
            pthread[i].id = i;
            pthread[i].pstr = str;
            pthread[i].length = N % (num - 1);
            pthread[i].ppstart = g_pp + i*(N / num + 1);
            phd[i] = _beginthread(searchthread, 0, &pthread[i]);
        }
    }
    WaitForMultipleObjects(num, phd, TRUE, INFINITE);
}

void main01()
{
    init(path);
    while (1)
    {//(1)处理大数据需要逻辑严谨
        char str[128] = { 0 };
        scanf("%s", str);
        //search(str);
        searchwiththread(str);
    }

    system("pause");
}





//01.多线程检索:分为多少步骤进行解决?
//      1.内存检索:
//          读取并加载DangDang.txt这个文本文件数据,形成内存数据库模型
//      2.文件检索:
//          将DangDang.txt这个文本文件分成22个文本文件,让单条线程处理
//          单个文本文件,采用缓冲区编程模式,对每个文本文件当中的每行数据
//          进行字符串检索动作
//02.多线程检索[内存+文件]+CGI=完整项目:
//      多线程检索文本文件-->一条线程处理一个文本文件
//03.多线程检索内存-->多线程检索文件:
//      1.形成内存数据库-->DangDang网数据全部入进堆内存
//      2.然后挨个挨个进行多线程并发访问堆内存当中每一段儿
//          微型的项目模块儿
//      3.多线程并发处理大数据的检索功能
//04.内存数据库模型:
//      1.获取文本文件的总行数
//      2.将文本文件当中的每一行字符串载入进堆内存,形成内存数据库模型
//          1)二级指针用于存储指针数组的首地址[存储指针]
//          2)对指针数组当中的每一个指针分配一块儿堆内存,[存储字符串]
//      3.多线程并发运行,各自检索各自区间当中的数据:
//          每条线程从分块儿部分开头进行逐步检索动作
//      4.多线程加速访问,充分利用CPU资源
//05.解决内存加载慢一级不够的情况:
//      1.采用多条线程分步进行文件数据加载进堆内存的动作
//      2.检索动作一旦完成就进行对堆内存的手动释放动作
//      3.最后再汇总多条线程的检索结果-->节约内存
//06.大数据处理过程当中经常出现的问题:
//      1.分配内存的同时需要进行清空动作
//          因为malloc();函数在进行堆内开辟的过程当中
//          会产生一些垃圾数据,然而并没有进行自动垃圾处理动作
//      2.最好使用calloc();会自动进行清空动作
//          指针内存的清空动作,防止垃圾地址保留
//07.多线程处理大数据:
//      单线程检索技术含量并不高-->多线程检索技术含量更高一些,效率也更高一些
//08.相当于一条线程处理一个文件:多线程检索文件&多线程检索内存的原理
//          线程与文件绑定在一起进行数据处理
//09.多线程加速检索数据:
//      1.加速处理方案-->效率非常的高
//      2.临界区-->信号量[可以解决同时操作一个文件的特点,但是不会同时处理一个数据]
///02.多线程多文件处理.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <string.h>
#include <memory.h>
#include <process.h>

//1.多条线程对应于多个文件,单条线程处理多个文件-->涉及到线程的调度问题-->很多设计模式都是多线程模式
//2.依赖于多线程进行实现的-->释放内存比较慢-->因为每条线程占用独立的堆内存空间
//3.将多个文件分配给多条线程处理之后-->能够解决内存不够的问题-->处理一段儿数据,清理一段儿内存
//4.排队进行运行-->结合队列进行-->排队进行多线程数据处理[队列应用]

//单个文件的处理信息封装
struct infos
{//(1)线程处理文件参数的封装
    char path[100];//该条线程所需处理的文本文件路径字符串-->预先设定
    int id;//线程标识ID-->没有了-->每一条线程处理一个文件-->线程标识
    char **g_pp;//把文件的内容载入进这个地址-->文件内容
    int length;//相当于根据一个文件创建字符串数组-->多少个字符串
    char findstr[256];//待处理文件路径-->原则上的位置:拷贝动作
}myinfo[22];//(2)全局变量,含有22个结构体元素的结构体数组,保存了22个文件的信息

HANDLE initthd[22] = { 0 };//22个初始化的线程地址
HANDLE findhd[22] = { 0 };//22个查找线程的地址

//01传入单个文本文件"处理信息"结构体,内存数据模型创建,为多线程检索数据做准备:
void runthreadinit(void *p)
{//(1)传递的是单个文本文件处理信息结构体-->线程的处理信息结构体
    struct infos *pinfo = p;
    FILE *pf = fopen(pinfo->path, "r");//(2)数据载入内存-->复杂操作
    //(3)原则上需要进行分开操作,这一条线程只是负责处理数据初始化-->载入内存操作
    if (pf==NULL)//(4)所有线程的本质都是为了操作数据
    {
        return;
    }
    else
    {//(5)判定一下有多少行
        int i = 0;//测试行数-->源源不断的读取下去
        while (!feof(pf))
        {
            char str[256] = { 0 };
            fgets(str, 255, pf);
            i++;
        }
        //(6)当前文件指针指向当前单个文本文件的末尾,需要将该文本文件指针调整到开头位置
        rewind(pf);//(7)设置文件指针回到开头的多种方式:rewind(pf);&reseek(pf,0,SEEK_SET);
        pinfo->g_pp = calloc(i, sizeof(char*)*pinfo->length);//(8)calloc();堆内存上面分配指针数组,不用进行memset();操作,不会存在垃圾字符,自动进行处理
        pinfo->length = i;//指针数组的长度-->也就是文本文件处理信息结构体的所描述的文件的数据行数
        for (int j = 0; j < i; j++)
        {//(9)扫描文件当中的每一行数据
            char str[256] = { 0 };
            fgets(str, 255, pf);
            int length = strlen(str);//(10)获取每一行的字符长度
            pinfo->g_pp[i] = calloc(length + 1, sizeof(char *));//分配内存并且自动完成数据初始化
            if (pinfo->g_pp[j]!=NULL)
            {
                strcpy(pinfo->g_pp[j], str);//拷贝内存,建立"内存数据库模型"
            }
        }
    }
    fclose(pf);
    printf("%d线程 init over\n", pinfo->id);
    //(11)逻辑流程:pinfo->path-->pinfo-->g_pp
}

//02执行多线程检索[传递的是文本文件"处理信息结构体",再执行单条线程检索]:
void runthreadsearch(void *p)
{
    struct infos *pinfo = p;//(1)文本文件处理信息结构体
    for (int i = 0; i < pinfo->length; i++)
    {//(2)遍历每行字符串,执行多线程检索方案
        if (pinfo->g_pp[i] != NULL)
        {//(3)检索动作在这里执行
            char *px = strstr(pinfo->g_pp[i], pinfo->findstr);
            if (px!=NULL)
            {
                printf("%s", pinfo->g_pp[i]);
            }
        }
    }
    printf("%d线程 find over\n", pinfo->id);//(4)展示执行该结构体搜索的处理线程标识号
}

//03释放内存数据库模型:
void freeall(struct infos *pinfo)
{//(1)多线程检索文本文件数据执行完毕之后,需要释放堆内存数据库
    //(2)注意释放内存数据库的操作流程
    //(3)指针数组当中的每一根指针所对应的内存区域处理
    //      释放顺序-->先释放内部堆内存,再释放外部堆内存
    printf("freeall start!");
    for (int i = 0; i < pinfo->length; i++)
    {//(4)释放指针数组的每一根指针所对应的堆内存
        free(pinfo->g_pp[i]);//(5)使用字符串数组所对应的指针进行堆内存释放
    }
    free(pinfo->g_pp);//(6)释放指向字符数组(一级指针数组)的二级指针所对应的内存空间
    printf("freeall end!");
}

//单线程测试数据检索:
void main()
{
    myinfo[0].id = 1;
    //(1)这里的字符串不能够进行直接的赋值动作
    strcpy(myinfo[0].path, "D:\\DangDang1.txt");
    //(2)线程的同步
    HANDLE pd1 = _beginthread(runthreadinit, 0, &myinfo[0]);
    //(3)查找之前必须要进行初始化完成
    WaitForSingleObject(pd1, INFINITE);//等待单个切割好的文本文件数据加载完成
                                       //(4)查找指定的数据
    strcpy(myinfo[0].findstr, "袁可");
    HANDLE pd2 = _beginthread(runthreadsearch, 0, &myinfo[0]);
    WaitForSingleObject(pd2, INFINITE);
    //(5)单点测试方案

    system("pause");
}

//多线程调度执行检索:
void main03()
{//(1)实现多线程的调度动作
    //(2)初始化22个不同的结构体[文本文件处理信息的拆分结构体]
    for (int i = 0; i < 22; i++)
    {//(3)逐步进行"文本文件处理信息拆分结构体"的初始化动作
        myinfo[i].id = i + 1;//(4)单个文本文件处理信息拆分结构体的多线程处理标识ID
        //(5)这里的sprintf();与strcpy();操作效果一样-->仅限处理效果一样
        sprintf(myinfo[i].path, "D:\\TestData\\DangDang%d.txt", i + 1);//(6)这里需要预先对大文本文件的数据进行拆分为多个文本文件,再多各个已经拆分好的文本文件进行单个处理信息结构体的封装
        //(7)拆分文件处理信息所需的检索字符串
        strcpy(myinfo[i].findstr, "吴伟");
    }//(8)整个文本文件的多线程待处理的拆分文件结构体处理信息封装完毕
    //(9)实现多线程调度操作
    for (int i = 0; i < 15; i++)
    {//(10)创建15条线程进行内存数据库模型的建立:采用多线程建立内存数据库模型
        initthd[i] = _beginthread(runthreadinit, 0, &myinfo[i]);
    }//(11)等待数据全部加载完毕完成之后在执行多线程检索动作
    //(12)载入文件的情况下,多线程不一定有优势,因为IO瓶颈的最大限度
    //      因为:一秒钟最多从磁盘读取30M的数据限制,读取瓶颈限制IO最大量
    //      这里只是为了多线程调度实现内存数据库模型的建立
    WaitForMultipleObjects(15, initthd, TRUE, INFINITE);
    //(13)下一步就是采用多线程进行数据的检索动作
    system("pause");//(14)暂定动作-->回车就开始执行多线程检索动作
    for (int i = 0; i < 15; i++)
    {//(15)多线程查询动作-->多线程依次结束-->多线程调度问题
        findhd[i] = _beginthread(runthreadsearch, 0, &myinfo[i]);
    }
    //(16)采用这种方式的意义就在于,一次只载入15个进行查询
    //      避免内存不够的情况
    //(17)释放动作内存数据库动作
    WaitForMultipleObjects(15, findhd, TRUE, INFINITE);
    system("pause");//(18)暂停动作,一旦回车就会自动释放内存数据库模型
    for (int i = 0; i < 15; i++)
    {
        freeall(&myinfo[i]);
    }
    system("pause");//(19)暂停动作,说明15个结构体的内存模型数据库释放完毕 
    //(18)载入剩下的7条线程数据,进行剩余的数据查询,然后逐步是吸纳剩下的7条线程调度处理
    //(19)队列扩展-->多线程的文件处理顺序调度
    for (int i = 15 ; i < 22; i++)
    {
        initthd[i] = _beginthread(runthreadinit, 0, &myinfo[i]);
    }
    //(20)注意首地址的处理特点
    WaitForMultipleObjects(7, initthd + 15, TRUE, INFINITE);
    system("pause");//(21)暂停动作,说明剩余7条线程待处理的数据载入内存,建立内存数据库模型的动作完毕
    for (int i = 0; i < 22; i++)
    {//(22)多线程的处理流程:
        //      1)初始化结构体数据
        //      2)结构体处理数据内容载入内存,建立内存数据库模型
        //      3)根据结构体数据的处理线程标识,进行多线程数据检索
        findhd[i] = _beginthread(runthreadinit, 0, &myinfo[i]);
    }
    WaitForMultipleObjects(7, findhd + 15, TRUE, INFINITE);
    system("pause");
    for (int i = 15; i < 22; i++)
    {
        freeall(&myinfo[i]);
    }
    //(23)内存不够的情况之下,分多次进行内存数据库的建立
    system("pause");
}



//01.内存不够的情况之下:
//      1.22个文件数据->内存不够-->连续内存处理-->多线程调度
//      2.数组管理多线程-->调度方案-->假如让一个线程开始干活儿
//02.解决将来内存不够使用的方式:
//      1.每一条线程处理一个文件-->将来还要处理17E的QQ数据
//      2.所以需要预先进行文件切割,再进行线程调度处理各个分割
//          之后的文件
//03.初始化和查询动作不放在一起比较好一些:
//      1.放在一起的话,需要不断的重启初始化模块儿,需要重新加载内存
//          非常耗费时间
//      2.所以需要先进行初始化-->再进行查询-->现在我们依赖于结构体数组进行数据查询
//04.现在需要测试每一个线程之后,再综合起来完成线程的调度
//05.假定你的内存只有512M的情况之下:
//      很难一口气将全部数据一并载入进堆内存-->所以需要多线程分块儿进行数据检索动作
//      单个:大数据文本文件数据-->划分为-->多个:小数据文本文件数据
//      然后将每个小数据文本文件分别分配给单个线程进行处理-->再实现多线程执行顺序调度
//      再进行调度&等待[合理调度线程]-->连续线程挨个进行小数据文件的检索动作
//      队列机制管理调度操作-->一次运行5个,然后释放-->继续接下来的5个线程操作
//06.先分割22个线程待处理的数据载入进内存,然后根据22个线程进行并行数据查询动作
//      如果内存足够,就直接启动所有线程,如果内存不够,就分批次执行多线程检索任务
//07.线程操作完毕之后,每条线程所指向的内存地址需要被释放掉
//      如果不进行释放,是不行的,会过渡占用堆内存
//08.内存数据库读取数据的特点:
//      IO读取数据次数非常多-->先进行待读取数据内容的载入进内存动作-->然后
//      操作系统将读取到的内容放入进缓存-->缓存操作:检索数据
//09.内存释放需要一点儿时间
//      内存的释放动作执行的很慢

程序片段(03):块读写.c
内容概要:freadfwrite

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

void main01()
{
    //(1)a[100]字符数组深度剖析:
    //      a:缓冲区编程当中的内存缓冲区首地址
    //      sizeof(int);单个元素的占用字节大小
    //      100:字符数组元素个数
    //      pfw:文本文件字符写入流
    int a[100];
    printf("\n%p", a);

    for (int i = 0; i < 100; i++)
    {
        a[i] = i;
        printf("\n%d", a[i] = i);
    }

    //(2)写入文件的扩展名可以进行任意指定
    FILE *pfw = fopen("D:\\TestData\\001.bin", "wb");

    //(3)返回写入文件文件成功的字符个数:
    //      成功:个数--失败:0
    int num = fwrite(a, sizeof(int), 100, pfw);
    printf("\nfwrite return=%d", num);
    fclose(pfw);
    //(4)结果:乱七八糟的内容
    //      记事本解析:0123456789-->ABCDEFGHIJKL
    //      文本解析:48-->0|65-->A

    system("pause");
}

//01.演示fwrite();函数的使用
void write()
{
    int a[100];
    for (int i = 0; i < 100; i++)
    {
        printf("\n%d", a[i] = i);
    }

    FILE *pfw = fopen("D:\\TestData\\002.bin", "wb");
    int num = fwrite(a, sizeof(int), 100, pfw);
    //(1)一个int类型数据占用4个字节,最终写入的结果为400个字节
    printf("\nfwrite return =%d", num);
    fclose(pfw);
}

//02演示fread();函数的使用
void read()
{
    void *p = malloc(400);
    printf("\n%p", p);
    FILE *pfr = fopen("D:\\TestData\\002.bin", "rb");
    int num = fread(p, 4, 100, pfr);
    printf("\nfread return=%d", num);
    fclose(pfr);
}

void main()
{
    write();
    void *p = malloc(400);
    printf("\n%p", p);
    FILE *pfr = fopen("D:\\TestData\\002.bin", "rb");
    //(1)文本和二进制的数据读取会产生一定的差别
    //      1).差距在于\n-->\r\n的识别特点:
    //      2).二进制文件数据读取准确无误,文本文件数据读取会减少
    //(2)函数介绍fread();的使用:
    //      p:缓冲区编程过程当中的,手动分配内存缓冲区的首地址
    //      4:单个内存缓冲区数组的元素大小
    //      100:内存缓冲区当中的元素个数
    //      pf:读取文件流
    int num = fread(p, 4, 100, pfr);
    printf("\nfread return=%d", num);
    //(3)这里的读取结果为26个
    //      1)成功的读取了26个[0-25]
    //      2)成功读取的元素个数
    //      397:垃圾字符
    fclose(pfr);
    //(4)fwrite();与fread();之间的区别:
    //      1).一般情况之下只是用于二进制读取写入操作
    //      2).如果操作文本文件的数据读取和写入操作就
    //          不可以使用这两个函数来进行数据操作
    //      3)返回值特点:
    //          fread();返回成功读取的个数
    //          fwrite();返回写入成功的个数
    //      4)二进制数据的分块儿处理方案

    system("pause");
}



//01.fread();&fwrite();特点:
//      1.内存与硬盘的特点:
//      2.fread();&fwrite();除了可以用于二进制文件读取以外
//          还可以用于二进制文件内部的排序操作
//      3.如何使用fread();&fwrite();进行读取和写入操作
//02.块儿读写操作:
//      内存内容直接写入到磁盘当中,和内存里边儿是一样的,所以可以
//      采取内存的方式进行数据操作[磁盘数据&内存数据一致=二进制]
//03.将一个数据写入到二进制文件当中:
//      int a[]在内存里边儿的存储方式:
//          a-->常量:地址-->右边儿按照"单个字节"进行解析的
//          0..1..2..3-->因为整数65-->字符A
//          编号为65的就是字符A|编号为48的就是字符0
//      将这些数据写入到文件的时候:
//          就户出现我们现在看到的内存效果
//      fwrite();与fread();操作的在内存当中操作的数据和
//          在硬盘当中操作的效果一致[内存数据&磁盘数据]
//04.将一个数据写入到二进制文件当中:
//05.fread()&fwrite();的区别:
//      1.参数说明:
//          buffer:内存缓冲区首地址
//          size:内存缓冲区当中的单个元素所占用的内存字节大小
//          count:内存缓冲区当中的元素个数
//      2.返回值特点:
//          (1).这两个函数的返回值都不能为负数
//          (2).返回值都是成功操作的元素个个数

程序片段(04):01.排序.c+02.排序.c
内容概要:文件排序以及二分查找法

///01.排序.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

//01二进制文件随机读写操作
void fileread()
{
    //(1)打开一个文件:
    FILE *pfr = fopen("D:\\TestData\\001.data", "rb");
    for (int i = 0; i < 100; i++)
    {
        int num;//(2)读取所有数据并进行显示-->每一行的每个数据读写操作
        //(3)还可以控制指针的位置进行数据的随机读写操作
        fseek(pfr, i*sizeof(int), SEEK_SET);//(4)正序操作采用SEEK_SET进行
        //(4)当i=99的时候,恰好是最后一个元素
        //fseek(pfr, i*sizeof(int), SEEK_END);//反向读取
        //(5)随机读写操作-->排序-->二分查找法
        //      这里fseek();的操作方式等价于随机读写操作
        //(6)10GB-->0.1s的查询操作-->查找方式的原理:随机读写
        //      二分查找法-->文件当中应用二分查找方法
        fread(&num, sizeof(int), 1, pfr);
        if (num==3)
        {
            printf("\n num=%d", num);//(7)顺序查找:最坏的情况之下一个都没有,100次,有一个99次,效率不高
        }//(8)这种方式操作的数据是连续的字节数据,而非文本文件的分行字符串操作方式
        printf("\n num=%d", num);
    }
    fclose(pfr);

    system("pause");
}

//02.二进制文件的二分查找法+随机读写操作
void binsearch(int findnum)
{
    FILE *pfr = fopen("D:\\TestData\\001.data", "rb");
    //(1)纯查找的情况之下不需要进行数据的写入操作
    int tou = 0;
    int wei = 100 - 1;
    int flag = 0;
    int ci = 0;
    while (tou<=wei)
    {
        ci++;
        int zhong = (tou + wei) / 2;
        //(2)数组里面直接a[zhong]操作就可以了
        //  但是文件里面就不同了-->操作模式变更
        int zhongdata;
        fseek(pfr, zhong*(sizeof(int)), SEEK_SET);
        if (findnum==zhongdata)
        {//(3)插值查找法的拓展
            flag = 1;
            printf("\n找到%d", zhongdata);
            break;
        }
        else if (findnum>=zhongdata)
        {
            tou = zhong + 1;
        }
        else
        {
            wei = zhong - 1;
        }
        printf("\n找了%d次!", ci);
    }
    if (flag)
    {
        printf("\n找到了");
    }
    else
    {
        printf("\n没有找到");
    }
    fclose(pfr);
}

void main01()
{
    //fileread();

    while (1)
    {
        int findnum;
        scnf("%d", &findnum);
        binsearch(findnum);
    }

    system("pause");
}

void randwrite()
{//(1)随机数生成代码
    time_t  ts;
    unsigned int num = time(&ts);//获取随机数种子
    srand(num);//下一步:rand();生成随机数

    int a[100];
    for (int i = 0; i < 100; i++)
    {
        a[i] = rand() % 100;
    }

    FILE *pfw = fopen("D:\\TestData\\sort.data", "wb");
    fwrite(a, sizeof(int), 100, pfw);
    //(2)这里是向sort.data写入100个int类型的元素,占用400个字节大小
    fclose(pfw);

    system("pause");
}

void showfile()
{
    int *p = malloc(400);
    FILE *pfr = fopen("D:\\TestData\\sort.data", "rb");
    fread(p, sizeof(int), 100, pfr);
    for (int i = 0; i < 100; i++)
    {
        printf("\n%d", p[i]);
    }
    fclose(pfr);
}

void main()
{
    randwrite();
    showfile();
    printf("\n排序之后!");//(1)二进制文件的读写操作
    FILE *pf = fopen("D:\\TestData\\sort.data", "rb+");
    for (int i = 0; i < 100-1; i++)
    {//(2)冒到最后一个元素的时候就不用进行排序动作了
        for (int j = 0; j < 100-1-i; j++)
        {//(3)冒一次,就有一个最大的数据沉底,下一次就可以少冒一次
            //if (a[j] < a[j + 1])
            //{//(4)针对于数组的操作方式
            //  swap(a[j], a[j + 1]);
            //}
            int dataj = 0;
            fseek(pf, sizeof(int)*j, SEEK_SET);
            fread(&dataj, sizeof(int), 1, pf);
            int dataj1 = 0;
            fseek(pf, sizeof(int)*(j + 1), SEEK_SET);
            fread(&dataj1, sizeof(int), 1, pf);
            if (dataj<dataj1)
            {//(4)这里是先读取缓冲区当中的数据,然后经过排序操作,输入到另外一个文件当中去[没有对内存缓冲区当中的内容直接进行操作]
                fseek(pf, sizeof(int)*(j + 1), SEEK_SET);
                fwrite(&dataj, 4, 1, pf);
                fseek(pf, sizeof(int)*j, SEEK_SET);
                fwrite(&dataj1, 4, 1, pf);
            }
        }
    }
    showfile();//(5)内存不够的情况下下,总文件大小10G
    //现有内存1G,怎么办?-->直接采用文件排序
    //-->将文件当做内存进行处理
    //-->内存映射文件的原理fread();&&fwrite();
    //          内存缓冲区<----->文件
    //-->CPU&内存-->fseek();&fread();-->指针移动&数据操作
    fclose(pf);
}





//五.二进制文件二分查找法:
//01.文件排序:在哪里进行文件排序?
//      1.直接在文件当中完成排序动作
//      2.原理:
//          (1).将原始文件当中的数据映射进自定义内存缓冲区当中
//          (2).对内存缓冲区当中的数进行排序动作
//          (3).然后将内存缓冲区当中排好序的数据写入到文件当中
//02.文本文件和二进制文件的区别?
//      直接操作文件读取情况之下:
//      1.文本文件的每一行长度都不一样:
//          fseek();可以随机移动到某个指针位置,但是不能够确定
//          该指针到底移动到了哪一行数据,不能够确定,因为每一行
//          的数据都是不等长的
//      2.二进制文件的fread();&fwrite();特点:
//          共同特点:
//              每移动4个字节,就等同于移动了1个int元素
//              每移动8个字节,就等同于移动了2个int元素
//              每移动12个字节,就等同于移动了3个int元素
//      3.总结特点:直接操作文本文件[将文本文件当做内存进行操作的前提情况之下]
//          对于文本文件的随机读写动作无法确定操作的是具体的哪一行?
//          但是对于二进制文件的话,就可以随机读写,并进行二进制排序操作
//          -->任意读取元素的操作:
//              任意读取元素之后-->进行排序操作
//              排序完成之后-->直接将数据写入到新文件当中
//              再直接对新文件当中的数据进行二分查找法的使用
//03.随机读写任意位置的数据:
//      1.二进制数据-->fseek(pf,0,SEEK_SET);-->偏移量决定
//      2.fseek(pf,sizeof(int)*i,SEEK_SET);-->读取任意位置的元素
//          根据字节偏移量进行数据的读取操作-->内存获取下标的方式
//          文件排序-->随机读写-->内存获取下标[冒泡排序-->快速排序]
//          文件排序-->一次读取一个,将其中的数据读取出来[插入排序]
//六.二进制数据内存排序:
//01.大数据处理流程:
//      1.将大数据信息逐个按行封装为结构体,组装成为结构体数组
//      2.在内存当中对结构体数组当中的每一个结构器进行排序操作
//      3.然后在内存当中对数据进行检索操作
//      4.再将结构体数组写入到一个二进制文件当中[后续操作]
//          以便于后期直接对文件的检索操作
//02.10G-->0.1秒的查询方式:
//      1.1000次-->二分查找法需要10次
//      2.内存映射文件:
//          二进制文件-->映射到内存当中
//          然后对内存当中的随机进行随机访问操作-->通过fseek();实现随机访问操作
//          文件的冒泡排序[从左往右,从右往左]
//03.大数据相关数据:
//      CSDN-->600万行-->排序之后再进行查询操作
///02.排序.c
#include <stdio.h>  
#include <stdlib.h> 
#include <time.h>

//内存排序:操作内存
//文件排序:操作文件

void main01()
{
    time_t ts;
    unsigned int num = time(&ts);
    srand(num);//(1)随机数范围控制:N个随机数=rand()%N;

    int a[100];
    for (int i = 0; i < 100; i++)
    {//(2)这里生成的随机数限定在0~99之间,总共是100个元素
        a[i] = rand() % 100;
    }

    FILE *pfw = fopen("D:\\TestData\\001.data", "wb");//(3)在这儿是属于二进制数据的写入模式
    fwrite(a, sizeof(int), 100, pfw);
    fclose(pfw);//(4)构造随机数文件成功-->垃圾字符
    //      (5)文件数据说明:
    //          48=>0----->0+7=>48+7=>55
    //          字符显示-->本质是int类型的数据-->这里只不过按照单字节进行解析的结果导致显示问题
    //          实质:直接采用的存储方式是二进制存储方式,而非[字符=>ASCII=>二进制数据=>存储]
    //          所以每个二进制数据都被解析成为了字符-->按照单个字节的ASCII方式进行解析的

    system("pause");
}

//01.对比函数:
int com(void *p1, void *p2)
{//(1)特别注意这里返回的是0与非0的数据,所以不会出现小于的结果
    //return (*(int*)p1)>(*(int*)p2);//成立[1]Or不成立[0]
    //(2)这人有失误的地方,返回值要么成立,要么不成立
    //  只有这么两种结果
    int *pint1 = p1;
    int *pint2 = p2;
    if (*pint1>*pint2)
    {//(3)排序需要分为三种情况
        return 1;
    }
    else if (*pint1<*pint2)
    {
        return -1;
    }
    else
    {
        return 0;
    }
}

//02.内存排序:将数据读取到内存,再对内存当中的数据进行操作
//对内存当中的结构体数组实现快速排序
void memsort()
{
    int *p = malloc(400);
    int *phead = malloc(400);

    FILE *pf = fopen("D:\\001.data", "rb+");
    fread(p, sizeof(int), 100, pf);
    for (int i = 0; i < 100; i++)
    {//(1)这里的数据解析方式,由用户自己进行决定
        //p[i]或者*(p+i)
        printf("\n%d", p[i]);
    }
    //(2)刷新内存缓冲区:强制将内存缓冲区当中内容刷入到存储结构当中
    fflush(pf);//强制动作-->排序操作
    qsort(p, 100, 4, com);//快速排序
    printf("\n sort later");
    //(3)将排序好的数据写回到文件当中
    for (int i = 0; i < 100; i++)
    {
        printf("\n %d", p[i]);
    }
    rewind(pf);//(4)使用rewind();将文件指针移动到开头位置
    fwrite(p, sizeof(int), 100, pf);//(5)将内存存储结构当中的数据,排序之后的数据写入到具体的文件当中
    fflush(pf);//刷新,生效//(6)强制刷新动作,将数据刷新到文件当中
    //(7)流程解释:先读取-->内存排序-->指针回到开头,
    //      再次将内存数据写入文件-->指针回到开头,
    //      将排序好的文件数据再次读取出来
    rewind(pf);
    fread(phead, sizeof(int), 100, pf);
    fflush(pf);//刷新,生效
    for (int i = 0; i < 100; i++)
    {
        printf("\n phead=%d", phead[i]);
    }
    fclose(pf);
}

void main02()
{
    memsort();

    system("pause");
}





//七.二进制数据内存排序:
//01.大数据的处理方式:
//      fread();&fwrite();两个函数的使用[既可以操作文件又可以操作内存]
//02.文件排序以及二分查找法:
//      1.主要解决排序-->查询
//      2.排序:两种方式[直接对文件排序&间接对内存排序]
//03.大数据处理方案:
//      1.内存排序-->文件排序
//      2.大数据处理流程:
//          (1)将文件当中的数据读取并载入进内存-->内存数据[未排序]
//          (2)对载入进内存当中的数据进行排序操作-->内存数据[排序]
//          (3)对内存当中排序之后的数据进行查询-->内存数据[查询]
//          (4)将内存当中排序之后的数据写入到文件-->便于以后的[文件检索]

程序片段(05):CSDN.c
内容概要:大数据线性处理

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct csdn
{//(1)行数据结构体封装
    char name[22];
    char password[43];
    char email[52];//(2)前期估计为20长度,但是经过后期的原始文件CSDN数据行长度的检测,最长长度为51,但是需要加上'\0'的长度,也就是52
};//(3)现在我们所设计的结构体还不算成熟,只是一种成员长度估算方式,正式开发的时候需要先扫描数据行当中的成员最长长度,需要获取MAX1+MAX2+MAX3

//1.遍历每一个字符串,只要遍历到的字符串长度比它大,就将其赋值过来
int namemax = -1;
int passmax = -1;
int emailmax = -1;

//01.将文本文件当中的每行字符串信息封装为一个结构体
void init(struct csdn *pdata, char *str)
{//(1)将每行字符串数据初始化指定的内存存储结构体当中
    for (char *p = str; *p != '\0'; p++)
    {//(2)单行字符串分离为每个字符
        if (*p=='#')
        {//(3)单行字符串预处理:用于采取自然切割法则
            *p = '\0';
        }
    }
    //(4)数据分离切割,初始化单个结构体
    strcpy(pdata->name, str);//(5)由于字符串的识别字符是'\0'字符的特点,所以可进行这样儿的操作,这种操作方式相对于指针操作方式要复杂一点儿
    //strcpy(pdata->password, str + strlen(str) + 1);//(6)实质就是指针操作原理
    //(7)另外一种就是采取指针操作方式,这是一种代码优化的操作方式
    char *pstr1 = str + strlen(str) + 1;
    strcpy(pdata->password, pstr1);
    char *pstr2 = pstr1 + strlen(pstr1) + 1;
    strcpy(pdata->email, pstr2);
    //(8)去除打印部分,因为但凡涉及到打印部分的效果都会比较慢
    //printf("%s,%s,%s", pdata->name, pdata->password, pdata->email);
}

//02.获取同类型字段的最大长度,特别注意去除了'\0'的长度
void getmax(char *str)
{
    for (char *p = str; *p != '\0'; p++)
    {//(1)对字符串进行分割字符预处理
        if (*p=='#')
        {
            *p = '\0';
        }
    }

    //(2)单行字符串根据'\0'字符串结尾符不断划分
    int max1 = strlen(str);
    if (namemax<max1)
    {
        namemax = max1;
    }

    char *pstr1 = str + strlen(str) + 1;
    int max2 = strlen(pstr1);
    if (passmax<max2)
    {
        passmax = max2;
    }

    char *pstr2 = pstr1 + strlen(pstr1) + 1;
    int max3 = strlen(pstr2);
    if (emailmax<max3)
    {
        emailmax = max3;
    }
}

//03读取并加载文本文件
void readfiletxt()
{//(1)获取每一行的最大值
    FILE *pfr = fopen("D:\\TestData\\DB\\CSDN.txt", "r");
    //(2)将文本文件转化到一个二进制文件再进行操作
    //      先以文本模式进行源文件数据读取,
    //      再以二进制模式进行数据写入新文件
    FILE *pfw = fopen("D:\\TestData\\CSDN.bin", "wb");
    if (pfr==NULL)
    {
        return;
    }
    else
    {
        while (!feof(pfr))
        {
            char str[256] = { 0 };
            fgets(str, 255, pfr);
            //getmax(str);//(3)这里是考虑最快的情况,获取每行的字符串数据信息,进行统计说明
            struct csdn csdn1;
            init(&csdn1, str);//(4)初始化每一个CSDN结构体数据,直到数据操作完毕再结束
            //(5)将内存当中以文本文件模式读取到的数据以二进制的形式写入进指定的新文件当中
            fwrite(&csdn1, sizeof(struct csdn), 1, pfw);
            //(6)经过这个操作之后,文件大大的变大了
            //      因为结构体为了进行对齐,会占用到很多空位
            //      所以导致文件占用字节数据变大[结构体空字节占用特点,为了结构体对齐]
        }
        fclose(pfr);
        fclose(pfw);
    }
}

//04.统计文件占用字节大小
int getfilesize(char *path)
{
    FILE *pfr = fopen(path, "rb");
    if (pfr==NULL)
    {
        return -1;
    }
    else
    {
        fseek(pfr, 0, SEEK_END);//(1)直接将指针移动到文件末尾
        int length = ftell(pfr);//(2)注意ftell以二进制模式打开文件进行字节占用大小的求取才准确,以文本模式打开的情况下,字节数目统计不会正确
        fclose(pfr);
        return length;
    }
}

void main()
{
    //(1)为单行数据构建堆内存结构体
    /*struct csdn csdn1;
    char str[100] = "bamy1 # 7618595 # bamy@etang.com";
    init(&csdn1, str);*/

    //(2)统计字符串当中指定字段的最大长度
    /*getmax();
    printf("%d,%d,%d", namemax, passmax, emailmax);*/

    //(3)获取文件的最大字节数[二进制读取的时候,是按照字节为单位进行统计的]
    //      最快的获取文件占用字节大小方式
    /*printf("%d", getfilesize("D:\\TestData\\DB\\CSDN.txt"));
    int size = getfilesize("D:\\TestData\\DB\\CSDN.txt");*/

    //(4)通过获取结构体的长度,来统计字符串的有效行数
    //printf("\n%d", size / sizeof(struct csdn));

    //(5)将文本文件当中的数据转存为二进制文件数据
    //readfiletxt();

    FILE *pfr = fopen("D:\\TestData\\CSDN.bin", "rb");
    while (1)
    {
        printf("请输入您要读取的第N个元素:");
        int N;
        scanf("%d", &N);
        struct csdn csdn1 = { 0 };
        //1)第N个元素的索引为N-1
        //2)这中间不管文件有多大,我们想读哪儿就读哪儿-->而且不会过度占用内存
        //3)因为所有的结构体大小都一样,所以读取的时候非常整齐
        fseek(pfr, sizeof(struct csdn)*(N - 1), SEEK_SET);//移动到指定位置进行二进制数据操作-->直接操作二进制文件
        fread(&csdn1, sizeof(struct csdn), 1, pfr);
        printf("\n%s,%s,%s", csdn1.name, csdn1.password, csdn1.email);
    }
    fclose(pfr);

    system("pause");
}





//八.CSDN数据&二进制数据&随机读写文件:
//01.大数据排序操作:
//      1.数据可随机读写的前提条件:二进制数据
//      2.文本文件-->转存-->二进制文件
//      3.在数据转存过程当中:
//          (1)交换-->排序:大量的数据移动操作
//          (2)中间:文件在内存当中都是完整排列的
//          (3)原因:文本文件当中的数据是长短不一
//          (4)数据对调太过麻烦-->累死人-->索引机制
//              索引价值没有按照二进制数据的访问访问方式快
//              但是索引机制可以表面过度的数据排序,调换动作
//02.索引机制&二进制机制:优劣对比
//      索引机制:
//          优点:
//              避免调换数据动作
//          缺点:
//              创建索引过程比较慢,间接访问
//      二进制机制:优势要相比索引机制高一些
//          优点:
//              内存&文件映射,直接访问
//          缺点:
//              排序,调换位置麻烦
//03.采用一个二进制数据进行标记:
//      索引机制:记录每行的元素个数
//          1--5-->索引对应关系:获取第N行数据
//          2--6-->必须知道前面数据的大小,才能够进行索引的区间划分
//          3--7-->这中间的效率类似于链表[层级关系,一级一级的,只不过无需记录地址]
//          ---->索引操作原理
//04.简单做法:
//      1.先将每行的数据对应的封装到每个结构体当中,形成结构体[数据行]数组
//      2.每个结构体的大小都是一样的,所以随机访问很方便
//      3.文件当中实现快速查找方案[索引对应于文件]
//      4.快排-->堆排-->全部用上-->索引排序
//05.大数据线性处理:
//      1.处理CSDN的Data数据
//      2.面试老前辈都有CSDN账户[数据广播]
//06.CSDN大数据处理流程:
//      1.对文本文件的数据进行按行读取
//          逐行生成结构体-->结构体数组-->存入文件
//      2.分离一行数据,进行数据测试:
//          (1)先完成单行字符串的测试,在进行多行字符串的测试
//          (2)先完成局部测试,在完成全局拆封是
//07.读取的最终长度统计为:正确的长度统计方式
//      namemax=21-->考虑到尾部还有结尾福的情况
//      passmax=42--->总结:22--43--52
//      emailmax=51
//08.挨个进行数据读取:
//      getmax();获取单行每列分字段最大长度-->最终进行统计
//09.载入内存再进行查询:
//      1.读取一个结构体,解析一个结构体,再进行查询动作
//      2.获取多大[总尺寸]-->判定行数
//10.存放到结构体当中还是可以进行排序:
//      这儿需要进行随机读写操作
//11.想获取那个就获取那个,想写那个就写那个:
//      如果已经排序之后,二分查找法更快
//      实现:二分查找+排序=717M-->简单

程序片段(06):索引.c
内容概要:索引与文本文件

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//1.为原始数据文本文件生成索引数据二进制文件:
//2.获取原始数据文本文件的总行数[实质是为了统计索引个数]-->a[N]
//3.获取原始数据文本文件当中每行的字符个数,包含了结尾回车符\r\n-->a[N]=width
//3.随机读取位置:fseek(首指针,偏移量,定点(定长));
//4.字符串的行结束符:'\r\n'
//5.安全检查:说明了函数漏洞问题&安全问题

//1.结构体抽象出来的数组存储结构:
struct indexs
{
    //(1)int index[];这里由于抽象数组的长度并不确定,所以建议将其定义为指针[指针所描述的长度也是不确定的]
    //(2)为了间接的模拟数组存储结构,所以需要使用struct进行索引数组信息的封装
    int *pindex;//数组堆内存
    int length;//数组总长度
}allindexs;

//01.获取原始数据文本文件当中的总数据行数-->其实是为了统计索引的个数
int getAllRows()
{
    int i = -1;
    //(1)rb读取方式比较精确,防止采用r方式进行数据的读取,因为r会有Windows操作系统上面的回车符长度读取变化问题[文本文件当中:\r\n-->读取出来:\n];
    //      rb方式进行任何数据的读取都不会出现读取识别错误的情况[Windows系统的内部原因,回车符识别错误问题],规避了\r\n回车符读取少一个字节的特点
    //(2)读取细节:
    //      读取的每一行字符串都有\r\n回车符,创建索引数据二进制文件的时候,需要进行\r\n的跳过处理,采用直接映射数据长度的特点,不让回车\r\n回车符占用存储空间
    FILE *pfr = fopen("D:\\TestData\\BaiDuPersons.txt", "rb");
    if (pfr == NULL)
    {
        return  -1;
    }
    else
    {
        i = 0;//初始化索引
        int alllength = 0;//初始化长度
        while (!feof(pfr))
        {//(3)feof();可以判断任何类型文件的结尾,该函数由操作系统进行封装,系统会自动记录文件的所有信息,包括结尾情况的处理[识别结尾]
            char str[1024] = { 0 };//自定义缓冲区的使用
            fgets(str, 1024, pfr);//(4)采取读取长度50,由于这个时候读取文件的时候采用的是二进制的读取模式,所以读取长度的时候不会跳过\r\n回车符,直到读取到\r\n结尾回车符的时候就自动进行换行动作,而且会将换行符进行拆分读取出来
            printf("\n%s", str);
            alllength += strlen(str);//(5)5->6:这里有一个特别重要的细节,当文本文件采用二进制的模式进行数据读取的时候,末尾的\r\n同样会被当做字符串按照一个一个的字节进行解析出来,所以长度不会发生变化
            //(6)alllength的结果:451--17[行数]
            //      原始:大小451字节-->读取:大小451字节&字符个数[文本文件按照二进制进行解析的时候,如果采用fgets();那么会将文本文件的二进制数据按照字节单位进行读取]
            printf("%d %d %s", strlen(str), i, str);
            i++;//(7)放在这里是一种优化解决方案:将容易发生错误的代码放置在前,如果发生错误,就可以少执行一步
        }
        printf("\n all=%d", alllength);
        fclose(pfr);
        return i;
    }
}

//02.读取原始数据文本文件,生成索引数据二进制文件
int initindexs()
{
    //(1)对原始数据文本文件当中的每一行生成索引数据二进制文件[过渡堆内存:结构体]
    int i = -1;
    FILE *pfr = fopen("D:\\TestData\\BaiDuPersons.txt", "rb");//(2)采用二进制读取方式,为了能够更好的统计总字节大小
    if (pfr==NULL)
    {
        return -1;
    }
    else
    {
        i = 1;//索引-->对应关系手动处理
        int alllength = 0;//尺寸
        while (!feof(pfr))
        {
            char str[1024] = { 0 };
            fgets(str, 1024, pfr);//(2)当fgets();函数读取到回车符的时候,由于是采用二进制的读取模式,所以会将\r\n以二进制解析,但是按照字符长度字节来进行字符串的解析,因此我们采用strlen();统计长度的时候是正确的,因为字符解析按照字节长度
            allindexs.pindex[i] = alllength;//(3)注意建立单个索引结构体的时候,索引&尺寸的创建顺序:这里是用于跳过表头的技巧
            alllength += strlen(str);//(4)统计原始数据文本文件当中一行所占用的字节总数
            i++;
            //(5)由于为0,所以可以直接从长度开始下一部分的数据操作
            //printf("\n=%d,index[%d]=%d,width=%d", i, i, allindexs.pindex[i], strlen(str));           
        }
        fclose(pfr);
        return i;
    }
}

void main()
{
    //(1)测试文件的总行数&总字节数:
    //printf("\n allrows=%d", getAllRows());

    //(2)测试原始数据文本文件生成的内存索引机制
    allindexs.length = getAllRows();
    printf("\n hang=%d", allindexs.length);
    allindexs.pindex = calloc(allindexs.length, sizeof(int));
    initindexs();
    //(3)随机读取原始数据文本文件当中的数据
    FILE *pfr = fopen("D:\\TestData\\BaiDuPersons.txt", "rb");
    while (1)
    {
        int num = 0;
        printf("\n请输入您所要查找的行索引:");
        scanf("%d", &num);
        fseek(pfr, allindexs.pindex[num], SEEK_SET);
        char str[1024] = { 0 };
        fgets(str, 1024, pfr);//读取数据,fgets();还是要以\r\n作为行数据的结尾进行判断
        printf("%s", str);
    }
    fclose(pfr);

    system("pause");
}





//九.QQ大数据文本随机读:
//01.二进制文件可以进行随机读写:
//      1.同样将文本文件采用二进制进行解析读取,也可以实现随机读写操作
//          但是需要原始数据文件的指针处理机制
//      2.原理:
//          (1)直接读取不可以-->因为字节数目不确定-->读取第N行的数据
//              但是不知道其的起始地址-->N行的首地址获取+行字节数统计
//          (2)解释:
//              1---5
//              2---6
//              3---7
//          (3)索引:实现随机读写的前提
//              行号:         行长度:
//              0                   8
//              1                   130
//              2                   120
//      3.索引机制的特点:
//          原始数据文本文件当中的每一行都具备占用两个字节的\r\n结尾回车符
//          特点:
//              从头开始进行读取,读取到第三个\r\n就是第三行-->\r\n就能够说明行数
//          结构:
//              类似于链表数据结构:
//                  要找到N必须先找到第N-1
//              文本阅读区原理-->随机打开大数据文件-->索引原理:
//                  标记起始地址的索引数组-->随机根据行号读取每行的起始地址
//                  -->然后一旦读取到\r\n就结束了
//02.大数据处理流程:
//      随机访问每一行-->前提是我们首先对每一行进行了排序操作:
//      原始数据文本文件排序-->查找-->最好的方式是读取一行,插入一行
//      插入排序法,插入完成之后,原始数据文件的副本是有序的
//03.排序和二分查找法:
//      实际上:文本和二进制都行,都可以实现排序和二分查找
//          只不过文本排序要比结构体排序麻烦,因为每行的长度不一样,
//          不一样的情况之下需要建立索引进行记录
//04.文件存储的很重要机制:
//      索引机制-->提高读取效率
//05.索引与文本文件:
//      二进制文件无需索引机制,但是文本文件却需要索引机制
//06.索引的原理:
//      处理大数据的开发流程:先进行小数据的测试,如果成功,
//      再进行移植和处理大数据-->文本复用测试
//07.不用添加目录的方式:
//      直接将文件数据放置到项目文件夹当中-->源文件目录
//08.注意文本文件的末尾回车符处理
//      数据复制的细节处理
//09.索引建立机制:
//      1.文本文件-->451Byte-->索引机制
//      2.建立索引-->Fseek();随机读写
//          最精准的方式是采用二进制的读取方式进行打开,因为排除结尾回车符产生的影响
//10.随机读取某一行的解决方案:
//      索引机制-->存储进数组当中进行处理
//11.实战阶段:
//      1.技术含量提升-->大数据处理-->垃圾数据:优化数据
//      2.随机读取某一行-->机那里一个索引,建立一个文件-->
//      3.然后直接读取索引-->效率,避免重复创建索引的操作
//12索引关系:每次都需要记录上一次的字节占用长度:
//      0           30          0
//      1           27          31
//      2           27          58
//13.建立索引文件-->直接读取索引:
//      longgave的原理:
//          大文件,内存很小,记事本打不开-->打不开的原因就在于数据量太大
//          无法完整的加载进内存-->索引进行索引读取[只读取一部分数据]

程序片段(07):大数据索引.c
内容概要:大数据索引

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>  
#include <stdlib.h>
#include <string.h> 
#include <memory.h>

//1.读取大数据的总行数:
//      int a[N]-->该数组不能存在于栈内存,会因为栈内存太小了,需要将该数组占用的内存结构开辟到堆内存中:
//2.将内存当中生成的索引数组结构体数据写入到文件当中,进行长期保存,随时查询-->对应于数据文件:
//3.将索引数据二进制文件当中的索引数据读取出来:
//      需要查询的时候,只需要先将索引文件载入进内存
//      然后根据内存当中的索引文件数据随机访问原始数据文件当中的随机行数据
//4.索引机制可以进行随机读取:
//      但是如果采用索引机制进行随机读写的话:
//          (1)首先会导致索引指向的原始数据文本文件变化-->索引数据文件变化
//          (2)数据文件变化-->索引文件同样变化[反正索引与文件一一对应,文件一变,数据则变]
//          (3)简单问题-->复杂化-->索引索引机制不利于随机写入操作:

//1.待操作文件的基本信息:
char path[256] = "D:\\TestData\\DB\\1EQQ.txt";//原始数据文本文件
char indexespath[256] = "D:\\TestData\\1EQQIndexes.txt";//索引数据二进制文件
#define N 84331542;//原始数据文件的总行数-->实质:待创建的索引个数

//2.采用结构体抽象的索引数组:
struct indexes
{
    //首地址+长度=>字节数+\r\n结尾:=>指针+字符串长度
    int *pindex;//结构体所抽象出来的数组指定索引位置
    int length;//结构体所抽象的数组总长度
}allindexes;

//01.初始化结构体所抽象出来的索引数组:
void init(char *path, char *indexespath)
{
    //(1)初始化结构体所抽象出来的索引数组
    printf("\n索引数组结构体开始初始化!");
    allindexes.pindex = calloc(84331542, sizeof(int));//(2)分配内存:一级指针指向的内存空间实质是0级指针数组,也就是所谓的变量数组
    allindexes.length = 84331542;
    printf("\n索引数组结构体结束初始化!");

    printf("\n开始读取原始数据文本文件,初始化内存当中索引数组结构体!");
    //(3)挨个进行按行读取原始数据文本文件,进行索引数组结构体的内存构造动作!
    FILE *pfr = fopen(path, "rb");
    if (pfr==NULL)
    {
        return;
    }
    else
    {
        int alllength = 0;
        for (int i = 0; i < 84331542; i++)
        {
            char str[1024] = { 0 };
            fgets(str, 1024, pfr);//(4)这里建立的索引可以是让字符之间完全没有结尾符&空格的情况
            allindexes.pindex[i] = alllength;//(5)错位现象:这里代表指针位置是从0开始的[也就是字节数从0开始进行的]
            int length = strlen(str);//(6)读取每行有多少个字符[实质:表面求取的是字符大小,表面求取的实质是字符大小,最终实质是统计指针的相对位置]
            alllength += length;//(7)表明下一个索引数据二进制文件当中索引所表示的原始数据文本文件当中的索引对应数据相对于起始指针的相对位置
        }
        fclose(pfr);
    }
    printf("\n结束读取原始数据文本问及那,初始化内存当中的索引数组结构体结束!");

    printf("\n开始向指定的索引数据文件写入索引数据!");
    FILE *pfw = fopen(indexespath, "wb");//(8)写入索引,在进行索引写入的时候,同样建议采用二进制的数据写入模式
    fwrite(allindexes.pindex, sizeof(int), allindexes.length, pfw);//(9)将整个索引数组结构体数据写入到二进制数据文件当中
    fclose(pfw);
    printf("\n结束向指定的索引数据文件写入索引数据!");

    //(10)释放内存当中索引数组结构体的堆内存结构:释放内存的时候我们必须手动释放在堆内存上面的存储空间,无需释放堆内存上面的内存空间
    free(allindexes.pindex);

    printf("\n开始读取索引数据二进制文件!");
    FILE *pfr1 = fopen(indexespath, "rb");
    if (pfr1==NULL)
    {
        return;
    }
    else
    {
        fread(allindexes.pindex, sizeof(int), allindexes.length, pfr1);//(11)真个索引数组结构体初始化
    }
    printf("\n结束读取索引数据二进制文件!");
}

//生成索引数据二进制文件
void main01()
{
    init(path, indexespath);

    system("pause");
}

//02快速将索引数据二进制文件载入内存,形成内存当中的索引数组结构体
void initindexes()
{
    //(1)创建内存索引数组结构体:
    printf("\n内存索引数组结构体开始创建!");
    allindexes.pindex = calloc(84331542, sizeof(int));//(2)按照索引顺序进行排列,每个索引对应于该索引指向的原始数据文本文件当中的开始字符数-->开始字节数-->相对于起始指针的相对长度
    allindexes.length = N;
    printf("\n内存索引数组结构体结束创建!");

    printf("\n开始将索引数组二进制文件当中的数据加载进内存,初始化内存索引数组结构体!");
    FILE *pfr = fopen(indexespath, "rb");
    fread(allindexes.pindex, sizeof(int), allindexes.length, pfr);
    fclose(pfr);
    printf("\n结束将索引数组二进制文件当中的数据加载进内存,结束初始化动作!");
}

//03慢速统计大数据文件的行数
int getN(char *path)
{
    int i = -1;
    FILE *pfr = fopen(path, "rb");
    if (pfr==NULL)
    {
        return -1;
    }
    else
    {
        i = 0;
        while (!feof(pfr))
        {//(1)84386121-->84358499-->说明自定义缓冲的大小如果没有大于或等于原始数据文本文件当中的宽度最宽行的长度-->就会导致行数统计过多!
            char str[1024] = { 0 };
            fgets(str, 1024, pfr);
            i++;
        }
        fclose(pfr);
        return i;
    }
}

void main02()
{
    //(1)获取长度-->开辟内存-->装上索引:行数-->索引
    //printf("%d", getN(path));

    //(2)将原始数据文件载入进内存生成内存索引数组结构体,然后再讲内存索引数组结构体二进制数据写入到索引二进制文件
    //init(path);

    //(3)将索引数组二进制文件当中的数据加载进内存当中的索引数组结构体
    initindexes();

    //(4)根据指定索引读取原始数据文本文件
    FILE *pfr = fopen(path, "rb");
    while (1)
    {
        int num = 0;
        printf("\n请输入要读取的行数:");
        scanf("%d", &num);
        fseek(pfr, allindexes.pindex[num], SEEK_SET);
        char str[128] = { 0 };
        fgets(str, 128, pfr);
        printf("%s", str);
    }
    fclose(pfr);

    system("pause");
}

void main()
{
    //(1).节约内存的大数据处理方案[索引机制]
    //(2).需要两个文件指针:
    //      一个读取索引文件,一个读取数据文件
    FILE *pf1 = fopen(indexespath, "rb");
    FILE *pf2 = fopen(path, "rb");
    //(3).根据索引进行快速读取原始数据文件的原理:
    //      这中间先进行索引的提取,再直接读取原始数据文件当中的索引指定数据行
    while (1)
    {
        int num = 0;
        printf("\n清输入您要读取的行数:");
        scanf("%d", &num);
        int indexnum = 0;
        //1).读取索引数据二进制文件
        //      由于:
        //          (1.索引数据二进制文件当中存放的是索引对应的字节起始点
        //          (2.然而每个字节起始点的存放单元都是占用一样的字节大小[int类型大小]
        //      索引:
        //          可以根据索引位置得出该索引所对应的字节起始点
        fseek(pf1, num*sizeof(int), SEEK_SET);
        fread(&indexnum, sizeof(int), 1, pf1);

        //fread(&indexnum, sizeof(int), 1, pf1);

        //(2)读取原始数据文本文件
        //      原理:直接根据字节相对于首指针的长度,跳到指定指针,进行数据的读取
        fseek(pf2, indexnum*sizeof(int), SEEK_SET);
        char str[128] = { 0 };
        fgets(str, 128, pf2);
        printf("\n%s", str);
    }
    fclose(pf1);
    fclose(pf2);

    system("pause");
}





//01.大数据索引:
//      1.根据索引读取数据存储地址[相对字节数]
//      2.大数据处理原理:索引对应机制
//02.处理流程:
//      1.建立索引
//      2.读取索引
//      3.利用索引
//03.编程流程:
//      1.读取原始数据文本文件总行数-->索引总数
//      2.根据索引总数,创建索引数组结构体
//      3.将索引数组结构体数据写入到索引数据二进制文件
//      4.根据指定索引读取索引数据二进制文件-->得到原始数据文件当中的字节相对数
//      5.根据字节相对数得到原始数据文本文件当中的待获取数据指针
//      6.根据数据指针获取原始数据文本文件当中的指定数据,自动结尾标识
//04.索引机制的缺点:
//      生成索引比较慢-->但是读取的时候比较快
//          84331542
//05.读取数据的时候很慢:
//      读取数据-->扫描数据-->随机访问-->释放内存
//      索引内存:321多M-->秒读获取原始数据文件当中的数据
//06.索引条件:
//      1.写入状态-->访问2GB的数据很快
//      2.释放内存[索引数组结构体]
//07.内存苦逼:
//      1.只是建立索引-->线性存储
//      2.可以不用操作内存-->直接进行文件的操作:
//          (1)双指针:
//              一个指针读取索引数据二进制文件,
//              一个指针读取原始数据文本文件
//          (2)相对字节:
//              中间状态需要获取相对字节数
//      3.原来是将原始数据文本文件当中的所有数据都直接载入进内存,
//          然后再对内存数据进行操作,这样操作效率比较高,但是占用内存,
//          但是现在只需要操作文件:只需要使用1M不到的内存就可以实现
//          指定数据的快速读取
//      4.索引机制原理:
//          索引数据二进制文件:索引-->字节相对数
//          原始数据文本文件:字节相对数-->数据
//          特点:
//              (1).直接读取-->速度极快-->效率极高
//              (2).大数据处理方式:算法技巧
//08.迁移CGI进行大数据处理:
//      有点:占用内存小+访问速速快

程序片段(08):main.c
内容概要:大文本数据排序二分查找

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define N 28

//1.指针数组
char **g_pp;

//2.索引数组
struct indexes
{
    int *pindex;
    int length;
}allindex;

//01统计行数:
int getN()
{
    FILE *pfr = fopen("file.txt", "r");
    if (pfr == NULL)
    {
        return -1;
    }
    else
    {
        int i = 0;
        while (!feof(pfr))
        {
            char str[50] = { 0 };
            fgets(str, 50, pfr);
            i++;
        }
        fclose(pfr);
        return i;
    }
}

//02.初始索引数组:
void initindexes(char *path)
{
    printf("\n索引数组开始分配!");
    allindex.length = N;
    allindex.pindex = calloc(N, sizeof(int));
    printf("\n索引数组结束分配!");

    printf("\n开始初始索引数组!");
    FILE *pfr = fopen("filesort.txt", "rb");
    if (pfr == NULL)
    {
        return;
    }
    else
    {
        int alllength = 0;
        for (int i = 0; i < N; i++)
        {
            char str[50] = { 0 };
            fgets(str, 50, pfr);
            allindex.pindex[i] = alllength;
            int length = strlen(str);
            alllength += length;
        }
        fclose(pfr);
    }
    printf("\n结束初始索引数组!");

    printf("\n开始向索引数据二进制文件写入索引数组结构体数据!");
    FILE *pfw = fopen("index.txt", "wb");
    fwrite(allindex.pindex, sizeof(int), allindex.length, pfw);
    fclose(pfw);
    printf("\n结束向索引数据二进制文件写入索引数组结构体数据!");

    free(allindex.pindex);

    printf("\n开始读取索引数据二进制文件!");
    FILE *pfr1 = fopen("index.txt", "rb");
    fread(allindex.pindex, sizeof(int), allindex.length, pfr1);
    fclose(pfr1);
    printf("\n结束读取索引数据二进制文件!");
}

//03.去除'\n',表明字符串结束:
void eatRN(char *str)
{//str指针副本
    while (*str != '\0')
    {
        if (*str == '\r' || *str == '\n')
        {
            *str = '\0';
        }
        str++;
    }
}

//04.内存数据库
void initmem()
{
    //(1)二级指针开辟指针数组
    g_pp = calloc(N, sizeof(char *));//分配指针数组,自动清零
    FILE *pfr = fopen("file.txt", "r");
    if (pfr==NULL)
    {
        return;
    }
    else
    {
        for (int i = 0; i < N; i++)
        {
            char str[50] = { 0 };
            fgets(str, 50, pfr);//读取指定长度
            g_pp[i] = calloc(strlen(str) + 1, sizeof(char));//分配字符内存
            sprintf(g_pp[i], str);//打印进去
            eatRN(g_pp[i]);//最佳做法,打印过去的时候去除掉回车符'\r''\n'
            printf("%s", g_pp[i]);//显示测试
        }                                                         
        fclose(pfr);
    }
}

//05.快排比较:
int com(void *p1, void *p2)
{
    char **pp1 = p1;
    char **pp2 = p2;
    return strcmp(*pp1, *pp2);
}

//06.排序操作:
void sort()
{//指针数组排序-->单线程最快的排序方式:快速排序
    qsort(g_pp, N, sizeof(char*), com);
}

void main()
{


    system("pause");
}


//
排序操作
//void sort()
//{//(1)指针数组排序-->单线程最快的排序方法:快速排序
//  qsort(g_pp, N, sizeof(char *), com);
//}
//
显示数据
//void show()
//{
//  printf("\n此时状态\n");
//  for (int i = 0; i < N; i++)
//  {
//      printf("\n%s", g_pp[i]);
//  }                                
//}
//
写入文件
//void writetofile()
//{
//  FILE *pf = fopen("D:\\TestData\\filesort.txt", "w");
//  for (int i = 0; i < N; i++)
//  {
//      char temp[100] = { 0 };
//      sprintf(temp, "%s\n", g_pp[i]);
//      fputs(temp, pf);
//  }
//  fclose(pf);
//}
//
读取索引
//void quick()
//{
//  printf("\n索引数组开始分配!");
//  allindex.length = N;
//  allindex.pindex = calloc(N, sizeof(int));
//  printf("\n索引数组完整分配!");
//
//  printf("\n开始索引创建!!");
//  FILE *pfr = fopen("D:\\TestData\\index.txt", "rb");
//  fread(allindex.pindex, sizeof(int), allindex.length, pin_ptr);
//  fclose(pfr);
//  printf("\n结束索引创建!");
//}
//
内存索引读取
//void main01()
//{
//  //int num = 0;
//  //scanf("%d", &num);
//
//  //printf("%d", getN());
//
//  /*initmem();
//  sort();
//  show();
//  writetofile();*/
//
//  //生成索引
//  init("D:\\TestData\\filesort.txt");
//
//  //加载索引
//  quick();
//
//  FILE *pf = fopen("D:\\TestData\\filesort.txt", "rb");
//  while (1)
//  {
//      printf("\n清输入您要读取的行数!");
//      int num = 0;
//      scanf("%d", &num);
//
//      fseek(pf, allindex.pindex[num], SEEK_SET);
//      char str[128] = { 0 };
//      fgets(str, 128, pf);
//      pritnf("\n%s", str);
//  }
//  fclose(pf);
//
//  system("pause");
//}
//
文件索引-->打开两个文件-->索引完成-->二分查找
//void main02()
//{
//  FILE *pf1 = fopen("D:\\TestData\\index.txt", "rb");
//  FILE *pfr2 = fopen("D:\\TestData\\filesort.txt", "rb");
//  while (1)
//  {
//      printf("\n清输入要读取的行数!");
//      int num = 0;
//      scanf("%d", &num);
//
//      int indexnum = 0;
//      fseek(pf1, num*sizeof(int), SEEK_SET);
//      fread(&indexnum, sizeof(int), 1, pf1);
//
//      fseek(pfr2, indexnum, SEEK_SET);
//      char str[128] = { 0 };
//      fgets(str, 128, pfr2);
//      printf("\n%s", str);
//  }
//
//  system("pause");
//}
//
//void eatg(char *str)
//{
//  while (*str!='\0')
//  {
//      if (*str=='-')
//      {
//          *str = '\0';
//      }
//      str++;
//  }
//}
//
两种方式:完整查找&只输入前面某一段儿
//void binsearch(char *searchstr)
//{
//  int tou = 0;
//  int wei = N - 1;
//  int flag = 0;
//  while (tou<=wei)
//  {
//      int zhong = (tou + wei) / 2;
//      char zhongstr[256] = { 0 };
//      {
//          FILE *pf1 = fopen("index.txt", "rb");
//          FILE *pf2 = fopen("filesort.txt", "rb");
//
//          int indexnum = 0;
//          fseek(pf1, zhong*sizeof(int), SEEK_SET);
//          fread(&indexnum, sizeof(int), 1, pf1);//读取索引zhong到indexnum
//
//          fseek(pf2, indexnum, SEEK_SET);
//          char str[128] = { 0 };
//          fgets(zhongstr, 128, pf2);
//      }
//      eatN(zhongstr);//因为是精准匹配查找
//      //整合zhongstr,便于部分查找
//      char *pnewzhongstr[256] = { 0 };
//      sprintf(pnewzhongstr, zhongstr);//只进行QQ号码的检索
//      eatg(pnewzhongstr);//遇到-就终止
//      //字符串的二分查找法不能够是用相等来进行操作
//      int res = strcmp(pnewzhongstr, searchstr);//-1 0 1
//      if (res==0)
//      {
//          flag = 1;
//          printf("%s",zhongstr);
//          break;
//      }
//      else if (res==1)
//      {
//          wei = zhong - 1;
//      }
//      else
//      {
//          tou = zhong + 1;
//      }
//  }
//  if (flag)
//  {
//      printf("\nfind");
//  }
//  else
//  {
//      printf("\nnot find");
//  }
//}
//
//void main()
//{
//  char str[256] = { 0 };
//  scanf("%s", str);
//  binsearch(str);
//
//  system("pause");
//}


//十.文本小数据二分查找:
//01.要点&细节:
//      1.文本文件-->配套索引-->指定读行:
//      2.文本文件-->排序副本-->二分查找
//          二分查找:实现二分查找的前奏就是排序[字符串排序]
//02.字符串排序是文件排序还是内存排序好?
//      1.文件排序和内存排序都可以!
//      2.文件排序:
//          如果采用文件排序,那么你的文件每修改一次,你的索引
//          同样需要修改一次[修改一次,就有可能移动很多次数据]
//      3.最佳方案:
//          先将原始数据文本文件当中的字符串数据拍好顺序
//          然后再对排序完成之后的原始数据文本文件进行查询
//03.QQ数据按照行数据进行排序:
//      1.只按照前面儿的QQ号码进行排序操作
//      2.大功能的编写流程:
//          先进行小部分代码测试,再进行大部分拼装-->移植大数据
//04.大文本数据排序以及二分查找:
//      判定行数-->读取内存-->内存排序-->写入文件
//      -->生成索引or二分查找
//      -->文本按照行进行快速查找-->轻松-->快速编写代码模式
//05.原理:
//      1.为指针数组的首指针-->声明二级指针
//      2.为指针数组当中的每一个一级指针,开辟数组存储字符数据
//      3.排序的时候是交换指针:
//          指针交换-->对应于文件交换-->写入数据-->完成排序
//          构建以一个有序文件-->建立索引-->二分查找
//06.测试函数可靠性:
//      1.注意排序特点-->数据操作&测试操作
//      2.没有索引是不行的-->创建索引-->才能实现二分查找
//          原始数据-->指针数组-->指针比较-->交换指针-->
//          按照指针-->指针数组当中的所存放的指针所对应的数据
//              是有序的,然后对将指针数组与索引进制进行联系
//07.二分查找法:两类
//      1.搜索整个QQ字符串-->全部搜索
//      2.搜索QQ号码-->只搜索QQ号码
//08.处理大数据的关键点完成了
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值