天天看點

C語言擷取多組資料,輸入輸出問題

前記

最近找實習時候初入門了OJ,平時做題的時候是直接寫函數的,但是筆試的時候是需要自己編函數,自己寫輸入輸出的,自己寫主函數的,結果卡殼了很久,耽誤了很多時間,就來總結一下。(小白發言,若有錯誤敬請指正)

擷取資料輸入(數值)

  • 在做筆試題時,常見給出多行輸入的情況,其中第一行會輸入以下幾行的行數或之類的資訊,然後将以下幾行放入一個一維或二維數組中。

例一:擷取一維數組

  • 輸入: 第1行為n代表使用者的個數 第2行為n個整數,第i個代表使用者标号為i的使用者對某類文章的喜好度。第3行為一個正整數q代表查詢的組數 第4行到第(3+q)行,每行包含3個整數l,r,k代表一組查詢,即标号為l<=i<=r的使用者中對這類文章喜好值為k的使用者的個數。 資料範圍n <= 300000,q<=300000 k是整型(此處隻讀到第三行)
  • 關鍵程式★★★★★:
//方法一:使用動态配置設定
	int *res = (int*)malloc(numsSize * sizeof(int));
	memset(res, 0, numsSize * sizeof(int));
//方法二:使用數組初始化
	int nums[numsSize];
	memset(nums,0,sizeof(int)*numsSize);
           
  • 使用for循環來循環存入: scanf("%d",&like[i]);
5
1 2 3 3 5
3
//下面的未編入
1 2 1
2 4 5
3 5 3
           
  • 對上述輸入可以先用scanf擷取行号和列号後再動态配置設定數組進行儲存。
#include<stdio.h>
#include<malloc.h>
int main(){
	int num=0,gnum=0;		//num代表的是n行,gnum代表的是使用者個數
	scanf("%d",&num);		//先讀到行數然後動态配置設定數組大小
	int *like=(int*)malloc(sizeof(int*)*num);
	memset(like, 0, num * sizeof(int));
	//将每行資料寫入like中
	for(int i=0;i<num;i++){
		scanf("%d",&like[i]);
	}
	//printf("num=%d,like[1]=%d\n",num,like[1]);
	scanf("%d",&gnum);		//讀到下一次輸入的組數
	//printf("%d",gnum);
} 
           
  • 運作結果為:
    C語言擷取多組資料,輸入輸出問題

例二:擷取二維數組

  • 将多行資料存入一個二維數組中。
3 3             //輸入第0行,表示 接下來要輸入n 行 m 列資料
1  2   3        //輸入第1行
11 12 13        //輸入第2行
21 22 23        //輸入第3行
           
  • 對上述輸入先用scanf擷取行号和列号後再動态配置設定數組進行儲存。
  • 關鍵程式★★★★★:
//動态配置設定存儲二維數組的大小,配置設定所有行的首位址
pArray = (int * * )malloc( n* sizeof(int *) ); 
// 按行配置設定每一列
pArray[i] = (int*)malloc( m * sizeof(int) );
scanf("%d", pArray[i] + j );
           
  • 使用雙層循環來循環讀入:scanf("%d", pArray[i] + j );
#include<stdio.h>
#include<malloc.h>
int main(){
	//初始化變量
	int n = 0, m = 0;
	int **pArray = NULL;
	int i, j;
	//擷取行列數
	scanf("%d %d", &n, &m );
	//動态配置設定存儲二維數組的大小
	pArray = (int **)malloc( n * sizeof(int*) );
	//使用雙層循環将資料讀入數組中
	for( i=0; i<n; i++)
	{
	      pArray[i] = (int*)malloc( m * sizeof(int) );
		for( j=0; j<m; j++)
		{
			scanf("%d", pArray[i] + j );
		}
	}
	//對讀入的資料進行測試
	printf("============ 輸入資料測試 ============\n");
	for( i=0; i<n; i++)
	{
		for( j=0; j<m; j++)
		{
			printf("%d ", pArray[i][j] );
		}
		printf("\n");
	}
} 
           
  • 輸出結果測試:
    C語言擷取多組資料,輸入輸出問題
    C語言擷取多組資料,輸入輸出問題
  • 解釋:scanf遇到空格會跳過,是以輸入不呈現3行3列時也可以正常讀取資料。
  • 特别注意:此處for循環中的scanf中的%d後面千萬不用加空格,加上後無法正确讀取。

例三:多組輸入,每行都是相同讀取個數

  • 輸入:第一行包括2個整數,N(1 <= N <= 1000),M(0 <= M <= N*(N-1)/2),代表現有N個人(用1~N編号)和M組關系;在接下來的M行裡,每行包括3個整數,a,b, c
  • 輸入多組資料(未知幾行),且每行資料個數固定的情況:★★★★★ (以下方式等價)
    • while(scanf("%d %d", &N, &M) != EOF)
    • while(~scanf("%d %d", &N, &M) )
    • 注釋:
      • (~為按位取反符号)

        EOF->-1 int 4byte 32bit

        原碼100000000000000000000000000000001

        反碼111111111111111111111111111111110

        補碼111111111111111111111111111111111

        整形在記憶體中存儲的是補碼

        ~-1;

        00000000000000000000000000000000即0

  • 關鍵程式:
    • while(scanf("%d %d", &N, &M) != EOF)
    • scanf("%d %d %d", &a, &b, &c);
include <stdio.h>
int main()
{
   int N, M;
    // 每組第一行是2個整數,N和M,至于為啥用while,因為是多組。
   while(scanf("%d %d", &N, &M) != EOF) {
      printf("%d %d\n", N, M);
      // 循環讀取“接下來的M行”
      for (int i=0; i<M; i++) {
        int a, b, c;
        // 每行是3個整數,a,b,c。
        scanf("%d %d %d", &a, &b, &c);
        printf("%d %d %d\n", a, b, c);
      }
      // M行讀取完了,就又要開始下一組了,去while那裡。
   }
}
           

例四:多組輸入,每行不同輸入個數

  • 根據給定的個數進行循環寫入即可。

後記

結合這幾個例子,大概可以覆寫輸入的問題,儲存至一維數組、二維數組以及多組輸入(未知個數)時該如何擷取輸入的問題。

擷取字元串輸入

  • 我個人感覺,scanf好用一點,因為可以直接跳過‘\n’,而用getchar的話需要再次調用取走緩沖區的‘\n’顯得有點麻煩的亞子。
  • 注意點:getchar讀取字元會傳回字元的ascii碼值,是以char,int都可,但傳回值一般選取int,因異常時會傳回EOF,而EOF是-1,在char放不下。

讀取多組字元串輸入

//即End Of File 表示檔案末尾 (多組輸入)
while ((ch = getchar()) != EOF)
//清理緩沖區的\n清除緩存區的\n
getchar();
           

讀取字元串輸入的兩種方式

  • 用getchar實作一個一個字元讀取。
  • 用gets實作一行字元讀取。
  • 注:如果不想用getchar來讀取\n進而消除的辦法,可以使用在%c前面加空格的方式來進行消除,此時寫作while (scanf (" %c", &ch) !=EOF)
//方法一:一個一個字元讀取
 while (((ch=getchar() != '0') && ch != EOF)
//方法二:讀取一行字元
gets(str);
           

總結及細節注意點

1.EOF是End of File的意思,在C語言中定義的一個宏,用作檔案結束标志。該宏定義在stdio.h中,從數值角度看,就是-1。#define EOF (-1)

  • 在Linux系統之中,EOF根本不是一個字元,而是當系統讀取到檔案結尾,所傳回的一個信号值(也就是-1)。
  • 一般C在讀取資料時,都是按流模式進行資料讀操作,這裡的流可以是檔案,也可以是标準輸入。即:EOF可以表示檔案結尾,還可以表示标準輸入的結尾。
  • 但是,标準輸入與檔案不一樣,無法事先知道輸入的長度,必須手動輸入一個字元,表示到達EOF。
  • Linux中,在新的一行的開頭,按下Ctrl-D,就代表EOF
  • Windows中,在新的一行的開頭,按下Ctrl-Z,就表示EOF。

2.scanf遇到空格就不讀了,getchar還會讀。在輸入緩沖區中scanf不會拿走輸入末尾的\n,是以還需要下一行輸入前,先用getchar取走留在緩沖區中的\n。如果不想用getchar來讀取\n進而消除的辦法,可以使用在%c前面加空格的方式來進行消除,此時寫作while (scanf (" %c", &ch) !=EOF))

注:scanf循環讀取時可以不考慮該問題,getchar會讀取到\n,是以在下次要用getchar前需要用getchar去掉\n才能讀取到對應字元串。

3.scanf需要加非格式字元,否則輸出不正常(非格式字元指描述語句,比如a=,b=)比如輸入:scanf(“a=%d,b=%d”,&a,&b);輸入時也需要加上a=,b=來描述對應,否則輸出不正常。

4.getchar讀取字元會傳回字元的ascii碼值,是以char,int都可,但傳回值一般選取int,因異常時會傳回EOF,而EOF是-1,在char放不下。

5.用getchar實作一個一個字元讀取,用gets實作一行字元讀取。

6.%s需要有結束标志,%c不需要。

7.printf傳回值為輸出字元的長度,是以有無\n是不同的,列印時(printf)有或沒有\n,資料長度是不一樣的。(有的話會+1)

8.判斷是不是字母可以直接用isalpha(ch)的方法,但是需要調用庫函數ctype.h

islower (ch)用來判斷是不是小寫字母。

isupper (ch)用來判斷是不是大寫字母。

toupper(ch));//小寫轉大寫

tolower(ch));//大寫轉小寫

參考文獻

OJ輸入輸出詳細講解

用scanf讀入多行資料

c中使用malloc動态申請二維數組

繼續閱讀