注册 登录
编程论坛 C++教室

如何在c程序里,读取两个文档疑问

cpp_初学者 发布于 2011-07-11 17:00, 773 次点击
文档1:
程序代码:

124
235
152
178
156
142
178
163
159

文档2:
程序代码:

124|5623
452|6698
178|9995
235|7542
159|8852
152|9963
156|8512
885|9956
754|6523

理想输出文档结果:
程序代码:

124|5623
235|7542
152|9963
178|9995
156|8512
142|
178|9995
163|
159|8852

分析数据逻辑:
程序代码:

1. 理想输出文档结果,以文档1的内容为模板;若文档2有相同数据,印出其数据;若不,保留空白;
2.理想格式:
程序名称 文档1名称 文档2名称 理想输出文档结果

我写的awk code:

awk -F"|" ' NR == FNR { arr[$1]=$2; next } {  print $1"|"arr[$1] } '  文档2名称 文档1名称 > 理想输出文档结果

我的awk,若是当文档1和2,数据相近时,没有分析上的问题。
但若文档1和2过大,这会遇到无法读取文档的问题 :(
多谢各位的意见.
17 回复
#2
pangding2011-07-11 21:05
文档不会又是几个 G 吧?
#3
pangding2011-07-11 21:11
是不是一定是文档一的数据比二的多?会不会有一行文档二里有,而一里没有的?
#4
cpp_初学者2011-07-12 11:01
回复 2楼 pangding
你好,版主 :)
我要读取的文档,没有那么大..
两个文档,各别最多大约(~400Mb)罢了
#5
cpp_初学者2011-07-12 11:03
回复 3楼 pangding
文档1的数据,有可能比文档2少...
但理想输出结果,一切介试文档1为模板...
数据里,是有可能文档2里有,而文档1没有的情况发生...
#6
succubus2011-07-12 13:23
程序代码:
#include <map>
#include <string>
#include <fstream>

using namespace std;

int main()
{
    map<string, string> outMap;
    ifstream fin1("doc1.txt");
    ifstream fin2("doc2.txt");
    ofstream fout("out.txt");
    string key;
    string word;
    string line;
    int delimiterPos;
    map<string, string>::iterator searchRes;

    while (fin1 >> key)
    {
        outMap.insert(make_pair(key, "|"));
    }

    while (fin2 >> line)
    {
        delimiterPos = line.find('|');
        key = line.substr(0, delimiterPos);
        word = line.substr(delimiterPos);
        if ((searchRes = outMap.find(key)) != outMap.end())
            searchRes->second = word;
    }

    for (map<string, string>::iterator iter = outMap.begin(); iter != outMap.end(); ++iter)
    {
        fout << iter->first << iter->second << endl;
    }

    return 0;
}
不要求输出文档的输出顺序和文档1一样的话上面这代码就够了
否则还得再加几句代码。。。
#7
cpp_初学者2011-07-12 14:42
回复 6楼 succubus
谢谢你的回复,succubus :)
这是我用您的程序,得到的结果:
程序代码:

124|5623
142|
152|9963
156|8512
159|8852
163|
178|9995
235|7542

输入文档1中“178”出现了两次.
但以上的结果,只出现一次的“178”.
您有任何意见,可以让结果页面如文档1一样,出现两次的“178”?
谢谢 :)
#8
succubus2011-07-12 14:50
以下是引用cpp_初学者在2011-7-12 14:42:04的发言:

谢谢你的回复,succubus :)
这是我用您的程序,得到的结果:
 
124|5623
142|
152|9963
156|8512
159|8852
163|
178|9995
235|7542
 
输入文档1中“178”出现了两次.
但以上的结果,只出现一次的“178”.
您有任何意见,可以让结果页面如文档1一样,出现两次的“178”?
谢谢 :)
程序代码:
#include <map>
#include <string>
#include <fstream>
#include <vector>

using namespace std;

int main()
{
    map<string, string> outMap;
    vector<string> keys;
    ifstream fin1("doc1.txt");
    ifstream fin2("doc2.txt");
    ofstream fout("out.txt");
    string key;
    string word;
    string line;
    int delimiterPos;
    map<string, string>::iterator searchRes;

    while (fin1 >> key)
    {
        keys.push_back(key);
        outMap.insert(make_pair(key, "|"));
    }

    while (fin2 >> line)
    {
        delimiterPos = line.find('|');
        key = line.substr(0, delimiterPos);
        word = line.substr(delimiterPos);
        if ((searchRes = outMap.find(key)) != outMap.end())
            searchRes->second = word;
    }

    for (vector<string>::iterator iter = keys.begin(); iter != keys.end(); ++iter)
    {
        searchRes = outMap.find(*iter);
        fout << searchRes->first << searchRes->second << endl;
    }

    return 0;
}
加个vector保存文档1的输入就行了
这样输出的顺序也可以和文档1的顺序保持一致,代码如上。
或者使用multimap代替map,multimap里的key可以出现多次,这个你可以自己试试。
#9
cpp_初学者2011-07-12 16:06
回复 8楼 succubus
谢谢您,succubus.
您所写的c++程序,得到和理想结果一样的数据 :)
要好好消化和理解您所写的程序。
处理较大的文档时,也是很快
非常感谢你的分享噢。
#10
succubus2011-07-12 16:21
不用客气
通过你的发帖看出你不是伸手党
所以我也乐于花点儿时间把代码给写出来供你参考
你可以找一些stl的资料看看差不多就能理解我写的了
STL的map用的数据结构实际上是红黑树,所以还是比较快的
#11
cpp_初学者2011-07-12 17:00
回复 10楼 succubus
非常感谢您的无私分享 :)
我之前对awk,sed,perl及基本的unix command,比较熟悉。
我多数都是用综合以上的语言,来处理文档。
但最近发觉,在处理某些较大的文档(>1Gb),我会遇到该程序需要花费很长的时间处理文档及没有足够记忆(RAM)分析文档。
因此,才希望了解c语言,是如何分析相同的文档。
我会再详读您的每行code,希望能理解您所用的逻辑。
谢谢
#12
cpp_初学者2011-07-12 17:31
回复 10楼 succubus
你好,我尝试篇写您的c程序,让其能自由读取任何输入的文档。
理想操作格式:

./cpp_program_name [input_file1_name] [input_file2_name] [output_file_name]

以下是我利用您的c程序,篇改的内容:
程序代码:

#include <stdio.h>
#include <string>
#include <map>
#include <fstream>
#include <vector>

using namespace std;

int main(int argc, char *argv[])
{
       map<string, string> outMap;
       vector<string> keys;

       char buf[4096];
       FILE *fin1, *fin2, *fout;

       if(argc != 4)
       {
               fprintf(stderr, "Usage:  ./cpp_program_name [input_file1_name] [input_file2_name] [output_file_name]\n", argv[0]);
               return(1);
       }

       fin1=fopen(argv[1], "r");
       if(fin1 == NULL)
       {
              fprintf(stderr, "Couldn't open %s\n", argv[1]);
              return(1);
       }
       fin2=fopen(argv[2], "r");
       if(fin2 == NULL)
       {
              fprintf(stderr, "Couldn't open %s\n", argv[2]);
              return(1);
       }


       fout=fopen(argv[3], "w");
       if(fout == NULL)
       {
              fprintf(stderr, "Couldn't open %s\n", argv[3]);
              return(1);
       }
    string key;
    string word;
    string line;
    int delimiterPos;
    map<string, string>::iterator searchRes;
    while(fgets(buf, 4096, fin1))
{
    while (fin1 >> key)
    {
        keys.push_back(key);
        outMap.insert(make_pair(key, "|"));
    }
}
    while(fgets(buf, 4096, fin2))
{
    while (fin2 >> line)
    {
        delimiterPos = line.find('|');
        key = line.substr(0, delimiterPos);
        word = line.substr(delimiterPos);
        if ((searchRes = outMap.find(key)) != outMap.end())
            searchRes->second = word;
    }
}
    for (vector<string>::iterator iter = keys.begin(); iter != keys.end(); ++iter)
    {
        searchRes = outMap.find(*iter);
        fout << searchRes->first << searchRes->second << endl;
    }
    fclose(fin1);
    fclose(fin2);
    fclose(fout);

    return 0;
}

当我试着解码以上程序时:
程序代码:

[home@cpp]g++ cpp_program.cpp
cpp_program.cpp: In function aint main(int, char**)a:
cpp_program.cpp:50: error: no match for aoperator>>a in afin1 >> keya
cpp_program.cpp:58: error: no match for aoperator>>a in afin2 >> linea
cpp_program.cpp:70: error: no match for aoperator<<a in afout << searchRes.std::_Rb_tree_iterator<_Tp>::operator-> [with _Tp = std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >]()->std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::firsta

您是否知道,该如何篇改,才能让您的c程序,依照理想操作形式的格式,运行及分析文档?
理想操作格式:

./cpp_program_name [input_file1_name] [input_file2_name] [output_file_name]
#13
pangding2011-07-12 19:54
没想到这样就可以了……
但用 stl 库,数据结构全是放在内存里的。楼主说内存不够使,这样的程序我本来以为会跑不下来。

succubus 的实现和我想的很相似,基本就是先把文档1的内容排一下序(map 是用红黑树实现的,所以二分查找的效果就会比较好),然后走文档2的时候好找关键字。

另外,楼主的错误提示里怎么感觉每行都有那么多 a 呀?是那个提示本来就是这样的吗?那把那个提示说的错误行,在代码里注一下。
#14
succubus2011-07-12 20:15
你改写后的程序fin1,fin2,fout已经是C库里的FILE*了
既然是C里的东西自然不可能重载运算符<<和>>。

另外你对awk,PERL等比较熟悉,其实也可以使用boost库,里面有正则表达式可以用
#15
cpp_初学者2011-07-13 19:27
回复 13楼 pangding
你好,以下三行,是错误的代码,我现在还在努力修正中
程序代码:

#include <stdio.h>
#include <string>
#include <map>
#include <fstream>
#include <vector>

using namespace std;

int main(int argc, char *argv[])
{
       map<string, string> outMap;
       vector<string> keys;

       char buf[4096];
       FILE *fin1, *fin2, *fout;

       if(argc != 4)
       {
               fprintf(stderr, "Usage:  ./cpp_program_name [input_file1_name] [input_file2_name] [output_file_name]\n", argv[0]);
               return(1);
       }

       fin1=fopen(argv[1], "r");
       if(fin1 == NULL)
       {
              fprintf(stderr, "Couldn't open %s\n", argv[1]);
              return(1);
       }
       fin2=fopen(argv[2], "r");
       if(fin2 == NULL)
       {
              fprintf(stderr, "Couldn't open %s\n", argv[2]);
              return(1);
       }


       fout=fopen(argv[3], "w");
       if(fout == NULL)
       {
              fprintf(stderr, "Couldn't open %s\n", argv[3]);
              return(1);
       }
    string key;
    string word;
    string line;
    int delimiterPos;
    map<string, string>::iterator searchRes;
    while(fgets(buf, 4096, fin1))
{
    while (fin1 >> key)
    {
        keys.push_back(key);
        outMap.insert(make_pair(key, "|"));
    }
}
    while(fgets(buf, 4096, fin2))
{
    while (fin2 >> line)
    {
        delimiterPos = line.find('|');
        key = line.substr(0, delimiterPos);
        word = line.substr(delimiterPos);
        if ((searchRes = outMap.find(key)) != outMap.end())
            searchRes->second = word;
    }
}
    for (vector<string>::iterator iter = keys.begin(); iter != keys.end(); ++iter)
    {
        searchRes = outMap.find(*iter);
        fout << searchRes->first << searchRes->second << endl;
    }
    fclose(fin1);
    fclose(fin2);
    fclose(fout);

    return 0;
}
#16
cpp_初学者2011-07-13 19:28
回复 14楼 succubus
谢谢你的细心帮忙和解释,我还在努力改正中
#17
specilize2011-07-13 20:27
我是这样想的,也是制作一个mulitimap容器,first值为文档一里面的每个值,second为一个指向文档二的文件指针,然后对着文档二里的每个值,查找在map容器中有无匹配的,有的话返回文件指针位置并使second值指向文档二的那个值,容器完成后,就可以输出了,不知道性能方面如何?
#18
specilize2011-07-13 20:33
说错了,second变量应该是文件读取位置的std::ios::pos_type变量
1