C++——数据流FILE,fscanf,容器vector,map,pair,rand,Makefile,main(int argc,char*argv[])

本文详细介绍C++中文件操作方法,包括文件读写、格式化读取等,并深入探讨了vector与map容器的使用技巧,如元素增删查改、迭代器应用等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文件处理

  • FILE * f1=open(‘file’,’r’) 读取文件file

  • FILE * f1=open(‘file’,’w’) 写入 文件file,如file不存在,则创建

  • fscanf(f1,正则表达式,输入列表);将f1文件指针指向的file,以正则表达式匹配,其中空格或tab会被跳过,将内容赋值给输入列表,每次只匹配一次正则表达式,并返回相应的匹配个数——当正则表达式与对应的数据不匹配时,将会返回错误的信息

    返回值:成功时返回从流中读取成功的数据的个数。失败时返回-1(Debian Linux)。

    1 原型

    int fscanf(FILE stream, const char format, …)

    功能: 从stream流中连续读取能够匹配format格式的字符到参数列表中对应的变量里。
    参数: “stream”是FILE指针类型变量,它指向一个真实的流对象。
    “format”是C字符串,由“空格”、“非空格”及“转换符”组成。具体格式为%[*][width][modifiers]type。见2.1与format参数相关的概念。
    “…”是与“format”中“转换符”对应变量地址的列表,两地址间用逗号隔开。
    返回值:成功时返回从流中读取成功的数据的个数。失败时返回-1(Debian Linux)。
    头文件:< stdio.h>

    2 与format参数相关的概念

    fscanf()的format参数允许的格式为:“%[*][width][modifiers]type”。[]中的内容根据需要供选,可缺省,%type必须要有,不可省。
    %:是format参数的起始符号,不可缺少

    
    以拥有3个数字行的最后一个数字决定拥有两个数字的行数
    2.2.1 fscanf()对空格的处理
    用fopen成功打开ffReadFile.dat文件后,单用以下语句读取文件的第一行内容:
    [cpp] view plaincopyprint?
    re  = fscanf(pF, "%d%d%d", &oLine.p, &oLine.l, &oLine.x);  
    if( 3 == re){  
            //Print on stdout  
            printf("%d\t%d\t%d\n", oLine.p, oLine.l, oLine.x);  
    }  
    re  = fscanf(pF, "%d%d%d", &oLine.p, &oLine.l, &oLine.x);
    if( 3 == re){
            //Print on stdout
            printf("%d\t%d\t%d\n", oLine.p, oLine.l, oLine.x);
    }
    
    

    此时,可以正确的打印第一行内容到屏幕之上。fscanf()函数读取第一行三个数据的过程如下:
    Figure2:fscanf()函数读取数据过程
    文件指针最开始指向ffReadFile.dat的第一个字符(即1)处,然后fscanf()根据“format”中的第一个“转换符”读取文件中的内容直到不符合当前“转换符”能够转换的字符为止,如果不符合“转换符”的数据为空格或者tab则舍掉空格或者tab使文件指针指向下一个数据。然后fscanf()继续在文件中匹配下一个“转换符”的数据。直到为所有的“转换符”匹配完数据为止

    2.2.2 fscanf()对不符合“特定格式”数据的处理
    当不符合“特定格式”所要求的数据且不为空格和tab时,fscanf()将匹配到符合“特定格式”的数据赋给地址后不再往下读。如现将ffReadFile.dat中的内容修改如下:

    3 使用fscanf()注意的地方
    确保检查返回值,以确定找到了期待的值。而使用%s 格式的时候,一定要小心缓冲区溢出

    参考:https://zhidao.baidu.com/question/126749593.html

    如:

    
    # include<stdio.h>
    
    
    # include<stdlib.h>
    
    int main()
    {
    int fd;
    long dev;
    long offset;
    long length;
    char ch;
    double ts=0.000000;
    if((fd=fopen("/home/haixian/ceshi/data.txt","r"))<0)
     {
       printf("open the file is error!\n");
       exit(0);
     }
    lseek(fd,0,SEEK_SET);
    
    while(5==fscanf(fd,"%ld,%ld,%ld,%c,%lf\n",&dev,&offset,&length,&ch,&ts))
     {//在这里就是第二个参数指定分隔参数的格式,在这里使用的是,来分隔。这样就很容易的获取了记录的各个字段的值并不需要自己编写函数来进行解析什么的。
       printf("%ld,%ld,%ld,%c,%lf\n",dev,offset,length,ch,ts);
    }
    close(fd);
    return 0;
    }

容器


1. vector

  • 头文件#include < vector>

  • 创建vector对象,vector< int> vec;

    • vector的元素不仅仅可以是int,double,string,还可以是结构体,但是:结构体要定义为全局的,否则会出错
  • Vector作为函数的参数或者返回值时,需要注意它的写法:

    double Distance(vector< int>&a, vector< int>&b) 其中的“&”绝对不能少

  • 尾部插入数字:vec.push_back(a);

  • 使用下标访问元素,cout<< vec[0]<< endl;记住下标是从0开始的。

  • 使用迭代器访问元素.

    vector<int>::iterator it;
    for(it=vec.begin();it!=vec.end();it++)
    cout<<*it<<endl;

  • 插入元素: vec.insert(vec.begin()+i,a);在第i+1个元素前面插入a;

  • 删除元素:vec.erase(vec.begin()+2);删除第3个元素;
  • vec.erase(vec.begin()+i,vec.end()+j);删除区间[i,j-1];区间从0开始
  • 向量大小:vec.size();
  • 清空:vec.clear();
vector算法
  • 使用reverse将元素翻转:需要头文件#include< algorithm>

    reverse(vec.begin(),vec.end());将元素翻转,即逆序排列!

(在vector中,如果一个函数中需要两个迭代器,一般后一个都不包含)

  • 使用sort排序:需要头文件#include< algorithm>

    sort(vec.begin(),vec.end());(默认是按升序排列,即从小到大).

可以通过重写排序比较函数按照降序比较,如下:

定义排序比较函数:
bool Comp(const int &a,const int &b)
    {return a>b;}
调用时:
    sort(vec.begin(),vec.end(),Comp)//实现降序排列

2.map< key_type,value_type>

  • 是一种键值对容器,< key,value>形式存在,类似于python中的字典,关键字key(在一个组map中key值存在唯一性),和关键字对应的值value

  • include< map>

  • 赋值形式如下:

    //头文件
    include< map>
    map<int, string> ID_Name;
    
    // 1,使用{}赋值是从c++11开始的,因此编译器版本过低时会报错,如visual studio 2012
    
    map<int, string> ID_Name = {
    
                  { 2015, "Jim" },
    
                  { 2016, "Tom" },
    
                  { 2017, "Bob" } };
    //2.使用[]赋值
    ID_Name[2014]='jock'
- 插入值:

  #include < iostream>
  #include < map>

  int main()
  {
      std::map< char, int> mymap;

      // 插入单个值
      mymap.insert(std::pair<char, int>('a', 100));
      mymap.insert(std::pair<char, int>('z', 200));

      //返回插入位置以及是否插入成功
      std::pair<std::map<char, int>::iterator, bool> ret;
      ret = mymap.insert(std::pair<char, int>('z', 500));
      if (ret.second == false) {
          std::cout << "element 'z' already existed";
          std::cout << " with a value of " << ret.first->second << '\ n';
      }

      //指定位置插入
      std::map<char, int>::iterator it = mymap.begin();
      mymap.insert(it, std::pair<char, int>('b', 300));  //效率更高
      mymap.insert(it, std::pair<char, int>('c', 400));  //效率非最高

      //范围多值插入
      std::map<char, int> anothermap;
      anothermap.insert(mymap.begin(), mymap.find('c'));

      // 列表形式插入
      anothermap.insert({ { 'd', 100 }, {'e', 200} });

      return 0;
  }
  • 取值方式:at和[]两种,使用at时,如果key不存在,则会报错,而不会打印[]值

    //ID_Name中没有关键字2016,使用[]取值会导致插入
    //因此,下面语句不会报错,但打印结果为空
    cout<<ID_Name[2016].c_str()<<endl;
    
    //使用at会进行关键字检查,因此下面语句会报错
    ID_Name.at(2016) = "Bob";

  • resize(n)

    调整容器的长度大小,使其能容纳n个元素。

    如果n小于容器的当前的size,则删除多出来的元素。

    否则,添加采用值初始化的元素。

  • resize(n,t)

    多一个参数t,将所有新添加的元素初始化为t。

参考:https://blog.youkuaiyun.com/shuzfan/article/details/53115922

pair

  • 定义 :pair< first_type,second_type> p

  • pair是一种模板类型,每个pair都可以存储两个值,两个值的属性类型无限制,主要为了方便返回两个值的函数,而不用定义struct的麻烦。

  • 赋值:

     pair<int ,int >p (5,6);
    
     pair<int ,int > p1= make_pair(5,6);
    
     pair<string,double> p2 ("aa",5.0);
    
     pair <string ,double> p3 = make_pair("aa",5.0);
  • 输出形式:利用pair的两个属性first和second(不是方法,是属性

    cout<<p1.first<<p1.second;

class 类

class类

  • 构造函数——赋值,无返回值,与类名同名
  • 析构函数——释放内存,无返回值,无参数,~类名
  • public,private,protected

rand

srand((unsigned) time(NULL));//保证每次运行随机性,不能在循环中使用
rand()#

makefile

文件内容:

all: file1,file2
file1:File1.cpp
    g++ File1.cpp -o fil1 -2
file2: File2.cpp
    g++ File2.cpp -o fil2 -2

在Makefile目录下只需执行

make all 即可执行整个编译

target: a.cpp,a.h
    g++ target

详细内容请参考:https://blog.youkuaiyun.com/ruglcc/article/details/7814546/

main(int argc, char *argv[])

  • argc是命令行总的参数个数,(注意:文件名本身也算一个参数),argc的值是在输入命令行时由系统按实际参数的个数自动赋予的。不需要手动添加。即表示argv[]的数组长度
  • argv[]是argc个参数,其中第0个参数是程序的全名,以后的参数命令行后面跟的用户输入的参数(字符串参数),传入的只是字符串,不是相应的‘file’内的内容,如:text a.c 只是传入两个字符串:argv[0] = “test”argv[1] = “a.c”,a.c内容如是否正确,是否真的存在不得而知
  • char *argv[]是一个字符串指针数组,其大小是int argc(系统自动统计),所以参数可以是任何形式的字符串,以空格相隔。

举例:test.cpp代码如下:

int main(int argc, char* argv[])
{
    int i;
    for (i=0;i<argc;i++)
        cout<<argv[i]<<endl;//输入数组到argv[i]中
    cin>>i;                 //输出数组argv[i]
    return   0;
}

比如你输入:
test a.c b.txt stringdata//实际参数为4个,即argc=4,argv长度为4(0-3)
输出如下:

test
a.c
b.txt
stringdata

即:
/*
argc = 4

argv[0] = “test”
argv[1] = “a.c”
argv[2] = “b.txt”
argv[3] = “stringdata”
*/

参考:https://blog.youkuaiyun.com/hopeneversleep/article/details/55798722

注:如需转载,篇头请务必标明出处及作者信息:
@洋石灰儿
@2018-5-21
@链接:https://blog.youkuaiyun.com/Yshihui/article/details/80388916

<think>嗯,用户在使用C语言的pthread程序时,遇到了load_sudoku函数无法正确读取命令行参数指定的txt文件的问题。我需要仔细分析可能的原因,并给出解决方案。首先,我应该回忆C语言中处理命令行参数的基本方法,以及文件操作的常见错误。 首先,用户提到的问题是关于命令行参数没有被正确读取。在C语言中,主函数的参数是argcargvargv[0]通常是程序名,argv[1]开始才是用户输入的参数。如果用户在运行程序时没有提供足够的参数,比如只输入程序名而没有指定文件名,那么访问argv[1]会导致越界,这可能是一个常见的问题。这时候程序可能会崩溃或者无法打开文件。 接下来,考虑文件路径的问题。如果用户提供的文件名不正确,或者路径不对,程序就无法找到文件。例如,如果文件不在当前工作目录下,而用户只提供了文件名,没有提供绝对路径或相对路径,那么fopen函数会失败。此外,文件权限问题也可能导致无法读取,比如文件没有读权限或者被其他程序占用。 然后是文件打开方式的检查。在代码中,使用fopen后,应该检查返回的FILE指针是否为NULL。如果fopen失败,可以通过perror输出具体的错误信息,帮助定位问题。例如,如果文件不存在,perror会显示“No such file or directory”。 另一个可能的问题是文件指针的使用错误。比如,在load_sudoku函数中,是否正确处理了文件的打开和关闭?是否在读取文件后没有正确关闭文件,导致后续操作出现问题?或者,在读取文件内容时,缓冲区溢出或者格式错误,导致读取的数据不正确。 此外,多线程环境下,比如使用pthread,是否存在线程间的竞争条件或者资源访问冲突?例如,多个线程同时尝试读取同一个文件,或者文件指针被错误地共享,导致读取混乱。不过,通常在加载数独谜题时可能是在主线程中完成,而不是在线程中,所以这可能不是主要原因,但需要确认代码结构。 可能的解决方案包括:检查命令行参数的数量,确保用户正确输入了文件名;验证文件路径和权限;在代码中添加错误处理,比如检查fopen的返回值并使用perror输出错误;确保文件读取逻辑正确,比如正确解析数独格式,逐行读取并处理数据;还有检查文件内容是否符合预期格式,避免解析错误。 例如,用户可能在代码中没有正确处理argc的值,导致当没有提供参数时访问了argv[1],从而引发段错误。应该在代码开头检查argc是否大于等于2,否则提示用法并退出。另外,文件读取时可能没有使用正确的模式,比如应该用"r"模式打开文本文件,而不是二进制模式。 另外,相对路径和绝对路径的问题。如果程序的工作目录和用户预期的不一致,比如在IDE中运行时,工作目录可能是项目目录而不是可执行文件所在的目录,这可能导致相对路径的文件找不到。这时候需要确认文件的实际位置,或者使用绝对路径进行测试。 还有,数独文件的格式是否正确?比如是否每行有恰好9个数字,或者是否符合程序预期的分隔符(如空格或逗号)。如果格式不匹配,可能导致读取错误,进而函数返回失败。 总结下来,可能的原因有: 1. 命令行参数未正确传递,导致无法获取文件名。 2. 文件路径错误或权限问题,导致文件无法打开。 3. 文件读取逻辑错误,如格式解析不正确。 4. 多线程环境中资源访问冲突(可能性较低,但需确认)。 解决方案包括: - 添加命令行参数数量的检查。 - 使用perror或strerror输出具体的文件打开错误。 - 检查文件路径是否正确,确认文件存在且有权限。 - 调试文件读取逻辑,确保按正确格式解析数独数据。 - 确保在load_sudoku函数中正确打开和关闭文件,处理可能的错误情况。</think>### 原因分析及解决方案 #### 1. 命令行参数解析错误 **现象**:程序未正确获取文件名参数,导致`fopen()`操作失败。 **验证方法**:在代码开头添加参数数量检查: ```c if (argc < 2) { printf("用法: %s <文件名>\n", argv[0]); exit(1); } ``` **解决方案**:通过`argv[1]`获取文件名时,需确保参数数量正确[^1]。 #### 2. 文件路径问题 **现象**:文件路径错误或权限不足导致无法访问。 **验证方法**:使用`perror()`输出错误详情: ```c FILE *fp = fopen(argv[1], "r"); if (!fp) { perror("文件打开失败"); exit(1); } ``` **解决方案**: - 使用绝对路径(如`/home/user/puzzle.txt`)测试 - 检查文件权限:`chmod +r puzzle.txt` #### 3. 文件读取逻辑错误 **现象**:缓冲区溢出或格式解析错误导致读取中断。 **验证方法**:添加文件内容调试输出: ```c char line[256]; while (fgets(line, sizeof(line), fp)) { printf("读取行: %s", line); // 检查实际读取内容 // 解析逻辑 } ``` **解决方案**:确保文件符合预期格式(如9x9数字矩阵,用空格/逗号分隔)。 #### 4. 线程资源竞争(低概率) **现象**:多线程同时访问文件指针导致冲突。 **解决方案**:将文件读取操作限制在主线程完成后再启动子线程[^2]。 --- ### 完整修复示例 ```c void load_sudoku(const char* filename, int grid[9][9]) { FILE *fp = fopen(filename, "r"); if (!fp) { perror("致命错误:文件打开失败"); exit(EXIT_FAILURE); } for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { if (fscanf(fp, "%d", &grid[i][j]) != 1) { fprintf(stderr, "文件格式错误于第%d行第%d列\n", i+1, j+1); fclose(fp); exit(EXIT_FAILURE); } } } fclose(fp); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值