好吧,各位,这是一个星期的时间,我学习如何用MATLAB做奇怪的事情。这周是DJing。我需要做的是弄清楚如何让我的函数输出歌曲的名字,歌曲的长度最接近剩下的时间。例如,如果我展示我的DJing技能,而我还有3:22,我必须选择一首长度最接近剩下时间的歌曲(可以是短的,也可以是更长的)。我有一个.txt文件可供选择。
Test Case
song1 = pickSong('Funeral.txt', '3:13')
song1 => 'Neighborhood #2 (Laika)'
The file for this looks like:
1. Neighborhood #1 (Tunnels) - 4:48
2. Neighborhood #2 (Laika) - 3:33
3. Une annee sans lumiere - 3:40
4. Neighborhood #3 (Power Out) - 5:12
5. Neighborhood #4 (7 Kettles) - 4:49
6. Crown of Love - 4:42
7. Wake Up - 5:39
8. Haiti - 4:07
9. Rebellion (Lies) - 5:10
10. In the Backseat - 6:21
我已经计划好了大部分,我遇到的问题是填充我的单元格数组。它只输入最后一首歌,然后在循环运行后将其更改为a-1。我尝试了三种不同的方法,最后一种是最复杂的(而且看起来很抱歉)。一旦我把单元格数组变成了正确的形式(作为完整的歌曲列表,而不仅仅是-1),我就应该清楚了。
function[song] = pickSong(file_name,time_remain)
Song_list = fopen(file_name, 'r'); %// Opens the file
Song_names = fgetl(Song_list); %// Retrieves the lines, or song names here
Songs_in = ''; %// I had this as a cell array first, but tried to populate a string this time
while ischar(Songs) %// My while loop to pull out the song names
Songs_in = {Songs_in, Songs};
Songs = fgetl(Song_list);
if ischar(Songs_in) %//How I was trying to populate my string
song_info = [];
while ~isempty(Songs_in)
[name, time] = strtok(Songs_in);
song_info = [song_info {name}];
end
end
end
[songs, rest] = strtok(Songs, '-');
[minutes, seconds] = strtok(songs, ':');
[minutes2, seconds2] = strtok(time_remain, ':')
all_seconds = (minutes*60) + seconds; %// Converting the total time into seconds
all_seconds2 = (minutes2*60) + seconds2;
song_times = all_seconds;
time_remain = all_seconds2
time_remain = min(time_remain - song_times);
fclose(file_name);
end
请并感谢您的帮助:)
麻烦的案件:
song3 = pickSong('Resistance.txt', '3:57')
song3 => 'Exogenesis: Symphony Part 2 (Cross-Pollination)'
1. Uprising - 5:02
2. Resistance - 5:46
3. Undisclosed Desires - 3:56
4. United States of Eurasia (+Collateral Damage) - 5:47
5. Guiding Light - 4:13
6. Unnatural Selection - 6:54
7. MK ULTRA - 4:06
8. I Belong to You (+Mon Coeur S'ouvre a Ta Voix) - 5:38
9. Exogenesis: Symphony Part 1 (Overture) - 4:18
10. Exogenesis: Symphony Part 2 (Cross-Pollination) - 3:57
11. Exogenesis: Symphony Part 3 (Redemption) - 4:37
发布于 2014-10-09 20:12:04
我将用你已经写的大部分内容来写一个答案,而不是建议一些完全不同的东西。虽然regexp
也很强大(我也喜欢正则表达式),但我发现它对于您目前所了解的东西来说太高级了,所以现在让我们放弃它吧。
这样,您就可以了解代码的错误所在,以及我的调试器有多棒(开玩笑)。当你在文本文件中阅读时,你所拥有的几乎是有效的。在创建存储所有字符串的单元格数组方面,您做了一个很好的选择。
我还将借用MrAzzaman先生在通过strtok
(可怕的工作)以秒为单位计算时间时的逻辑。
另外,我要稍微改变一下你们的逻辑,这样我才能理解我将如何去做。以下是基本算法:
-1
,我们就没有更多的歌曲可读了,所以退出循环。:
分隔。下一个只会包含歌曲本身的名字。现在,我们遍历从步骤3创建的单元格数组中的每个元素。(A)为了填充第一个单元格数组,我使用strfind
查找all出现的-
字符所在位置。一旦我找到了这些发生的位置,我就会选择最后一个位置,即-
发生的位置。我用它来索引我们的歌曲字符串,跳过两个字符来跳过-
字符和空格字符。我们从这一点开始提取所有的字符到行的末尾来提取我们的时间。
(b)要填充第二个单元格数组,我再次使用strfind
,但随后确定空间发生的位置,并选择第一个空格发生的位置的索引。这与歌曲编号与歌曲音轨之间的差距相对应。使用(a)中索引的结果,我从第一个空格的索引中跳过一个字符到索引-
字符前的索引,以成功地获得歌曲。这是因为在-
字符之前的歌曲标题的最后一个字之间可能会有一个空格,所以我们想要删除这个空格。strtok
,就像您使用过的那样,并将字符串由:
拆分。MrAzzaman也使用了它,我将借用他的逻辑来计算每一次所需的总秒数。min
函数,并使用函数的第二输出。这将告诉数组中的发生了最小值。因此,我们只需搜索每首歌的剩馀时间和经过的时间之间的最小差异。请注意,您说过您不关心您是否过去或在过去的时间。您只需要最近的时间。在这种情况下,您需要获得时间差的绝对值。假设你有一首歌花了3:59,另一首歌是6:00,剩下的时间是4:00。假设你的曲目中没有4:00长的歌曲,你会想要选择3:59的歌曲。但是,如果您从较长的轨道(6:00)减去剩余的时间,您将得到一个负差,min
将返回此轨道.不是3:59的那首歌。这就是你需要取绝对值的原因,所以这会忽略你是在剩余的时间内还是在剩余的时间内。下面是代码,不需要再多说了:
function [song] = pickSong(file_name, time_remain)
% // Open up the file
fid = fopen(file_name, 'r');
%// Read the first line
song_name = fgetl(fid);
%// Initialize cell array
song_list = {song_name};
%// Read in the song list and place
%// each entry into a cell array
while ischar(song_name)
song_name = fgetl(fid);
if song_name == -1
break;
end
song_list = [song_list {song_name}];
end
%// Now, for each entry in our song list, find all occurrences of the '-'
%// with strfind, and choose the last index that '-' occurs at
%// Make sure you skip over by 2 spaces to remove the '-' and the space
song_times = cell(1,length(song_list));
song_names = cell(1,length(song_list));
for idx = 1 : length(song_list)
idxs = strfind(song_list{idx}, '-');
song_times{idx} = song_list{idx}(idxs(end)+2:end);
idxs2 = strfind(song_list{idx}, ' ');
%// Figure out the index of where the first space is, then extract
%// the string that starts from 1 over, to two places before the
%// last '-' character
song_names{idx} = song_list{idx}(idxs2(1)+1 : idxs(end)-2);
end
%// Now we have a list of times for each song. Tokenize by the ':' to
%// separate the minutes and times, then calculate the number of seconds
%// Logic borrowed by MrAzzaman
song_seconds = zeros(1,length(song_list));
for idx = 1 : length(song_list)
[minute_str, second_str] = strtok(song_times{idx}, ':');
song_seconds(idx) = str2double(minute_str)*60 + str2double(second_str(2:end));
end
%// Now, calculate how much time is remaining from the input
[minute_str, second_str] = strtok(time_remain, ':');
seconds_remain = str2double(minute_str)*60 + str2double(second_str(2:end));
%// Now, choose the song that is closest to the amount of time
%// elapsed
[~,song_to_choose] = min(abs(seconds_remain - song_seconds));
%// Return the song you want
song = song_names{song_to_choose};
%// Close the file
fclose(fid);
end
在上面给出的两个例子中,这是我得到的输出。我冒昧地创建了自己的文本文件,其中包含了您的音乐(非常棒的品味):
>> song1 = pickSong('Funeral.txt', '3:13')
song1 =
Neighborhood #2 (Laika)
>> song2 = pickSong('Resistance.txt', '3:57')
song2 =
Exogenesis: Symphony Part 2 (Cross-Pollination)
发布于 2014-10-10 21:25:11
以下是我的实现:
function song = pickSong(filename, time_remain)
% read songs file into a table
t = readSongsFile(filename);
% query song length (in seconds)
len = str2double(regexp(time_remain, '(\d+):(\d+)', ...
'tokens', 'once')) * [60;1];
% find closest match
[~,idx] = min(abs(t.Duration - len));
% return song name
song = t.Title(idx);
end
function t = readSongsFile(filename)
% read the whole file (as a cell array of lines)
fid = fopen(filename,'rt');
C = textscan(fid, '%s', 'Delimiter',''); C = C{1};
fclose(fid);
% parse lines of the form: "0. some name - 00:00"
C = regexp(C, '^(\d+)\.\s+(.*)\s+-\s+(\d+):(\d+)$', 'tokens', 'once');
C = cat(1, C{:});
% extract columns and create a table
t = table(str2double(C(:,1)), ...
strtrim(C(:,2)), ...
str2double(C(:,3:4)) * [60;1], ...
'VariableNames',{'ID','Title','Duration'});
t.Properties.VariableUnits = {'', '', 'sec'};
end
我们应该从测试文件中得到预期的结果:
>> pickSong('Funeral.txt', '3:13')
ans =
'Neighborhood #2 (Laika)'
>> pickSong('Resistance.txt', '3:57')
ans =
'Exogenesis: Symphony Part 2 (Cross-Pollination)'
注意:上面的代码使用MATLAB表来存储数据,这样可以方便地操作。例如:
>> t = readSongsFile('Funeral.txt');
>> t.Minutes = fix(t.Duration/60); % add minutes column
>> t.Seconds = rem(t.Duration,60); % add seconds column
>> sortrows(t, 'Duration', 'descend') % show table sorted by duration
ans =
ID Title Duration Minutes Seconds
__ _____________________________ ________ _______ _______
10 'In the Backseat' 381 6 21
7 'Wake Up' 339 5 39
4 'Neighborhood #3 (Power Out)' 312 5 12
9 'Rebellion (Lies)' 310 5 10
5 'Neighborhood #4 (7 Kettles)' 289 4 49
1 'Neighborhood #1 (Tunnels)' 288 4 48
6 'Crown of Love' 282 4 42
8 'Haiti' 247 4 7
3 'Une annee sans lumiere' 220 3 40
2 'Neighborhood #2 (Laika)' 213 3 33
% find songs that are at least 5 minutes long
>> t(t.Minutes >= 5,:)
% songs with the word "Neighborhood" in the title
>> t(~cellfun(@isempty, strfind(t.Title, 'Neighborhood')),:)
发布于 2014-10-09 17:10:51
您可以使用textscan
来管理这一点,如下所示:
function[song,len] = pickSong(file_name,time_remain)
fid = fopen(filename);
toks = textscan(fid,'%[^-] - %d:%d');
songs = toks{1};
song_len = double(toks{2}*60 + toks{3});
[min_rem, sec_rem] = strtok(time_remain, ':');
time_rem = str2double(min_rem)*60 + str2double(sec_rem(2:end));
[len,i] = min(abs(time_rem - song_len));
song = songs{i};
请注意,只有当您的歌曲名称中没有“-”字符时,这才会有效。
编辑:这是一个解决方案,(应该)在任何歌曲标题上工作:
function[song,len] = pickSong(file_name,time_remain)
file = fileread(file_name);
toks = regexp(file,'\d+. (.*?) - (\d+):(\d+)\n','tokens');
songs = cell(1,length(toks));
song_lens = zeros(1,length(toks));
for i=1:length(toks)
songs{i} = toks{i}{1};
song_lens(i) = str2double(toks{i}{2})*60 + str2double(toks{i}{3});
end
[min_rem, sec_rem] = strtok(time_remain, ':');
time_rem = str2double(min_rem)*60 + str2double(sec_rem(2:end));
[len,i] = min(abs(time_rem - song_lens));
song = songs{i};
regexp
是一个MATLAB函数,它在字符串上运行正则表达式(在本例中是歌曲名文件)。字符串'\d+. (.*?) - (\d+):(\d+)\n'
扫描每一行,提取每首歌曲的名称和长度。\d+
匹配一个或多个数字,而.*?
匹配任何数字。括号用于对输出进行分组。因此,我们有:
match n digits, followed by a (string), followed by (n-digits):(n-digits)
括号中的所有内容都作为单元格数组返回给toks
变量。for循环只是从结果单元格数组中提取歌曲名称和长度。
https://stackoverflow.com/questions/26290154
复制相似问题