如何編寫 C 程序在 Linux 上創(chuàng)建音樂播放列表
使用我在 Linux 上制作的這個(gè) C 程序在旅途中聆聽你喜愛的歌曲。
我最近在 Linux 中編寫了一個(gè) C 程序,從我廣泛的 MP3 庫(kù)中創(chuàng)建一個(gè)較小的隨機(jī) MP3 文件選集。該程序會(huì)遍歷一個(gè)包含我的 MP3 庫(kù)的目錄,然后創(chuàng)建一個(gè)包含隨機(jī)的、較小的歌曲選集的目錄。然后我將這些 MP3 文件復(fù)制到我的智能手機(jī)上,以便隨時(shí)隨地收聽。
瑞典是一個(gè)人口稀少的國(guó)家,有許多農(nóng)村地區(qū)沒有完整的手機(jī)覆蓋。這就是在智能手機(jī)上擁有 MP3 文件的原因之一。另一個(gè)原因是我并不總是有錢購(gòu)買流媒體服務(wù),所以我喜歡擁有自己喜歡的歌曲的副本。
你可以從它的 Git 倉(cāng)庫(kù) 下載我的應(yīng)用。我專門為 Linux 編寫了它,部分原因是在 Linux 上很容易找到經(jīng)過良好測(cè)試的文件 I/O 例程。多年前,我嘗試使用專有的 C 庫(kù)在 Windows 上編寫相同的程序,但在嘗試文件復(fù)制時(shí)遇到了困難。Linux 使用戶可以輕松直接地訪問文件系統(tǒng)。
本著開源的精神,我沒費(fèi)多少力氣就找到了 Linux 的文件 I/O 代碼來激發(fā)我的靈感。我還發(fā)現(xiàn)了一些啟發(fā)了我的分配內(nèi)存的代碼。我編寫了隨機(jī)數(shù)生成的代碼。
該程序的工作方式如下所述:
- 詢問源目錄和目標(biāo)目錄。
- 詢問存放 MP3 文件的目錄下的文件個(gè)數(shù)。
- 搜索你希望復(fù)制的收藏的百分比(從 1.0% 到 88.0%)。如果你有 1000 個(gè)文件的集合,并希望從你的集合中復(fù)制 125 個(gè)文件而不是 120 個(gè)文件,你也可以輸入 12.5% 之類的數(shù)字。我將上限設(shè)置為 88%,因?yàn)閺?fù)制超過 88% 的庫(kù)將基本生成與你的基礎(chǔ)庫(kù)相似的庫(kù)。當(dāng)然,代碼是開源的,因此你可以根據(jù)自己的喜好自由修改。
- 使用指針和malloc 分配內(nèi)存。一些操作需要內(nèi)存,包括代表音樂收藏中文件的字符串列表。還有一個(gè)列表來保存隨機(jī)生成的數(shù)字。
- 生成所有文件范圍內(nèi)的隨機(jī)數(shù)列表(例如,如果集合有 1000 個(gè)文件,則為 1 到 1000)。
- 復(fù)制文件。
其中一些部分比其他部分更簡(jiǎn)單,但代碼只有大約 100 行:
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h> /* include necessary header files */
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#define BUF_SIZE 4096 /* use buffer of 4096 bytes */
#define OUTPUT_MODE 0700 /*protect output file */
#define MAX_STR_LEN 256
int main(void) {
DIR *d;
struct dirent *dir;
char strTemp[256], srcFile[256],
dstFile[256], srcDir[256], dstDir[256];
char **ptrFileLst;
char buffer[BUF_SIZE];
int nrOfStrs=-1, srcFileDesc,
dstFileDesc, readByteCount,
writeByteCount, numFiles;
int indPtrFileAcc, q;
float nrFilesCopy;
// vars for generatingRandNumList
int i, k, curRanNum, curLstInd,
numFound, numsToGen, largNumRange;
int *numLst;
float procFilesCopy;
printf("Enter name of source Directory\n");
scanf("%s", srcDir);
printf("Enter name of destionation Directory\n");
scanf("%s", dstDir);
printf("How many files does the directory with mp3 files contain?\n");
scanf("%d", &numFiles);
printf("What percent of the files do you wish to make a random selection of\n");
printf("enter a number between 1 and 88\n");
scanf("%f", &procFilesCopy);
// allocate memory for filesList, list of random numbers
ptrFileLst= (char**) malloc(numFiles * sizeof(char*));
for (i = 0; i < numFiles; i++) {
ptrFileLst[i] = (char*)malloc(MAX_STR_LEN * sizeof(char));
}
largNumRange = numFiles;
nrFilesCopy = (procFilesCopy / 100) * numFiles;
numsToGen = (int)((procFilesCopy / 100) * numFiles);
printf("nrFilesCopy=%f", nrFilesCopy);
printf("NumsToGen=%d", numsToGen);
numLst = malloc(numsToGen * sizeof(int));
srand(time(0));
numLst[0] = rand() % largNumRange + 1;
numFound=0;
do {
curRanNum = (int)rand() % largNumRange + 1;
if (numLst[0] == curRanNum) {
numFound=1;
}
} while(numFound == 1);
numLst[1] = curRanNum;
getchar();
curLstInd = 1;
i = 0;
while(1) {
do {
numFound = 0;
curRanNum = (int)rand() % largNumRange + 1;
for (int k = 0; k <= curLstInd; k++){
if (numLst[k] == curRanNum)
numFound = 1;
}
} while(numFound == 1);
numLst[curLstInd+1] = curRanNum;
curLstInd++;
i++;
// numsToGen=Total numbers to generate minus two
// already generated by the code above this loop
if (i == (numsToGen-2))
break;
}
d = opendir(srcDir);
if (d) {
while ( (dir = readdir(d)) != NULL ) {
strcpy(strTemp, dir->d_name);
if (strTemp[0] != '.') {
nrOfStrs++;
strcpy(ptrFileLst[nrOfStrs], strTemp);
}
}
closedir(d);
}
for (q = 0; q <= curLstInd; q++) {
indPtrFileAcc = numLst[q];
strcpy(srcFile, srcDir);
strcat(srcFile, "/");
strcat(srcFile, ptrFileLst[indPtrFileAcc]);
strcpy(dstFile, dstDir);
strcat(dstFile, "/");
strcat(dstFile, ptrFileLst[indPtrFileAcc]);
srcFileDesc = open(srcFile, O_RDONLY);
dstFileDesc = creat(dstFile, OUTPUT_MODE);
while(1) {
readByteCount = read(srcFileDesc, buffer, BUF_SIZE);
if (readByteCount <= 0)
break;
writeByteCount = write(dstFileDesc, buffer, readByteCount);
if(writeByteCount <= 0)
exit(4);
}
//close the files
close(srcFileDesc);
close(dstFileDesc);
}
}
這段代碼可能是最復(fù)雜的:
while(1) {
readByteCount = read(srcFileDesc, buffer, BUF_SIZE);
if (readByteCount <= 0)
break;
writeByteCount = write(dstFileDesc, buffer, readByteCount);
if (writeByteCount <= 0)
exit(4);
}
這將從指定的文件中讀取多個(gè)字節(jié)(readByteCount)到字符緩沖區(qū)中。該函數(shù)的第一個(gè)參數(shù)是文件名(srcFileDesc)。第二個(gè)參數(shù)是一個(gè)指向字符緩沖區(qū)的指針,這之前在程序中聲明過。該函數(shù)的最后一個(gè)參數(shù)是緩沖區(qū)的大小。
程序返回讀取的字節(jié)數(shù)(在本例中為 4 個(gè)字節(jié))。如果返回的數(shù)字為 0 或更少,則第一個(gè) if 子句會(huì)跳出循環(huán)。
如果讀取字節(jié)數(shù)為 0,則所有寫入完成,循環(huán)中斷以寫入下一個(gè)文件。如果讀取的字節(jié)數(shù)小于 0,則發(fā)生錯(cuò)誤并退出程序。
當(dāng)讀取 4 個(gè)字節(jié)時(shí),它會(huì)寫入它們。write 函數(shù)接受三個(gè)參數(shù)。第一個(gè)是要寫入的文件,第二個(gè)是字符緩沖區(qū),第三個(gè)是要寫入的字節(jié)數(shù)(4 個(gè)字節(jié)) .該函數(shù)返回寫入的字節(jié)數(shù)。
如果寫入了 0 個(gè)字節(jié),則發(fā)生了寫入錯(cuò)誤,因此第二個(gè) if 子句退出程序。
while 循環(huán)讀取并復(fù)制文件,一次 4 個(gè)字節(jié),直到文件被復(fù)制。復(fù)制完成后,你可以將隨機(jī)生成的 mp3 文件的目錄復(fù)制到你的智能手機(jī)。
復(fù)制和寫入例程相當(dāng)有效,因?yàn)樗鼈兪褂?Linux 中的文件系統(tǒng)調(diào)用。
改進(jìn)代碼
該程序很簡(jiǎn)單,可以在用戶界面和靈活性方面進(jìn)行改進(jìn)。例如,你可以實(shí)現(xiàn)一個(gè)計(jì)算源目錄中文件數(shù)量的函數(shù),這樣你就不必手動(dòng)輸入它。你可以添加選項(xiàng),這樣你就可以非交互地傳遞百分比和路徑。但是代碼做了我需要它做的事情,它是 C 編程語言簡(jiǎn)單效率的演示。