1. 方法indexOf()返回一个关键字(或词)在字符串中的位置,它有一个引数:关键字。例如下例将返回数值3:
String search = "def"; String toBeSearched = "abcdefghi"; int index = toBeSearched.indexOf(search);
2. 逐一println()一下下边诸例,数一数(记住一个字符串的第一位是0而不是1,因为他是一个数组,所以你应当从0数起),你便会了解它的用法:
String sentence = "The quick brown fox jumps over the lazy dog." ; println(sentence.indexOf("quick")); println(sentence.indexOf("fo")); println(sentence.indexOf("The")); println(sentence.indexOf("blah blah"));
3. 是的,试到上边最后一个例子的时候,你会发现它返回的值是-1。这是一个很好的选择,因为在数组序号中不存在-1,因此,当indexOf()找不到一个关键字的时候,返回值-1来表述是再合适不过的。
4. 一个字符串的一部分被称为子串(substring),一个子串可以藉由substring()函数得到。它有两个引数:开始和结束的位置。比如下例的结果为“def”:
String alphabet = "abcdefghi" ; String sub = alphabet.substring(3,6);
5. 通过上例我们发现,子串开始于给定的开始位置(3),却结束于给定的结束位置减一(6)。减一的好处是,如果你需要的子串结束于字符串末尾,你可以方便的使用thestring.length(),同时,你可以用结束位置减去起始位置来简单的计算子串的长度。
6. 你可以这样从整个字符串中获取到”quick brown fox jumps over the lazy dog”:
String sentence = "The quick brown fox jumps over the lazy dog."; int foxIndex=sentence.indexOf("fox jumps over the lazy dog"); int periodIndex = sentence.indexOf("."); println(sentence.substring(foxIndex,periodIndex));
当然,如果你愿意数的话,你也可以这样写:
String sentence = "The quick brown fox jumps over the lazy dog."; println(sentence.substring(16,(sentence.length()-1)));
第二个方式,虽然现在看起来比第一种写得更少,但是设想一下如果要在一篇文章中截取某个部分的话,硬数还不数死你?!
7. 下例实现了用户键盘输入录入:
PFont f; // 保存输入文字的变量 String typing = ""; // 保存回车后保存文字的变量 String saved = ""; void setup() { size(300,200); f = createFont("Arial",16,true); } void draw() { background(255); int indent = 25; // 设置字体和文本颜色 textFont(f); fill(0); // 显示一切 text("点击这个小程序并用键盘输入。\n敲回车保存你的输入。", indent, 40); text(typing,indent,90); text(saved,indent,130); } void keyPressed() { // 如果回车键按下, 保存并清除字符串 if (key == '\n' ) { saved = typing; // 可以通过将一个字符串设置等于""实现将其清除 typing = ""; } else { // 否则,串联字符串 // 用户输入的每个字符都被加到String变量之后. typing = typing + key; } }
8. p5提供了其他关于字符串组合及拆分的函数:split()和join()。顾名思义,split()用来将一个长字符串拆解为一个字符串数组,它有两个引数:将被拆解的字符串和分界符(delimiter):
// 用空格作为界定符拆分一个字符串 String spaceswords = "oNEwAY is a Post Rock band from Kunming." ; String[] list = split(spaceswords, " "); for (int i = 0; i < list.length; i++) { println(list[i] + " " + i); }
或者用逗号作为界定符:
// 用逗号作为界定符拆分一个字符串 String commaswords = "oNEwAY,is,a,Post,Rock,band,from,Kunming." ; String[] list = split (commaswords, ','); for (int i = 0; i < list.length; i++) { println(list[i] + " " + i); }
二者运行后在信息窗口均返回结果:
oNEwAY 0
is 1
a 2
Post 3
Rock 4
band 5
from 6
Kunming. 7
9. 如果要使用多于两个分界符,你需要使用splitTokens()函数,它与split()的用法基本一致,除了一点区别:字符串中的任何元素都有作为一个界定符的机会:
// 多分界符拆分字符串 String stuff = "hats & apples, cars + phones % elephants dog."; String[] list = splitTokens(stuff, "& , + ."); for (int i = 0; i < list.length; i++) { println(list[i] + " " + i); }
运行后信息窗口返回结果:
hats 0
apples 1
cars 2
phones 3
% 4
elephants 5
dog 6
由此可见,作为界定符的元素将不会出现在数组中。
10. 如果你要拆分一个字符串中的数字,可用int()函数将结果数组转化为一个整数数组:
// 计算字符串内各数字之和 String numbers = "8,67,5,309"; // 转化字符串数组为整数数组 // 字符串中的数字并非数字并不可用于数学计算,除非事先将它们转为数字类型 int[] list = int(split(numbers, ',')); int sum = 0; for (int i = 0; i < list.length; i++) { sum = sum + list[i]; } println(sum);
11. join()的过程与split()正相反,它是将一个字符串数组整合为一个长字符串。同样是两个引数:欲结合数组,分隔符。
假设这样一个数组:
String[] lines = {"It","was","a","dark","and","stormy","night."};
用“+”和for循环,我们可以这样连接它们:
// 手工连接 String onelongstring = " "; for (int i = 0; i < lines.length; i++) { onelongstring = onelongstring + lines[i] + " "; }
使用join(),一切得到最大简化:
String onelongstring = join (lines, " ");
12. p5可以读取一个外部txt档:
String[] lines = loadStrings("text.txt"); println ("there are " + lines.length + " lines"); for (int i = 0; i < lines.length; i++){ println(lines[i]); }
是的,仍然,作为常识,你需要将text.txt文件放入这个sketch下的data文件夹内。
13. 下例为一个可视化效果的初级例子,将data.txt中的数据转化为柱状图显示:
13.1. 创建并保存data.txt,内容(当然,你可以随便写):131,85,87,16,169,140,153,72,113,123
13.2. 用p5运行如下代码:
int[] data; void setup() { size(200,200); // 文件中的文字被载入一个数组. String[] stuff = loadStrings("data.txt"); // 这个数组只有一个元素因为它只有一行. // 用逗号作为分界符,并将数组转化为整数数组 data = int(split(stuff[0], ',' )); } void draw() { background(255); stroke(0); for (int i = 0; i < data.length; i ++ ) { // 数组元素被用于设置矩形的颜色和高度. fill(data[i]); rect(i*20,0,20,data[i]); } noLoop(); }
14. 继续一个稍微高级的例子,将文本文件内的数据传入一个对象的构造器:
Bubble[] bubbles; void setup() { size(200,200); smooth(); // 以字符串数组的方式载入文本文件 String[] data = loadStrings("data.txt"); // 气泡对象数组的大小由文本文件中总的行数决定 bubbles = new Bubble[data.length]; for (int i = 0; i < bubbles.length; i ++ ) { // 每行都被拆分为一个小数数组. float[] values = float(split(data[i], "," )); // 数组中的值被传递入气泡类的构造器. bubbles[i] = new Bubble(values[0],values[1],values[2]); } } void draw() { background(255); // 显示并移动所有气泡 for (int i = 0; i < bubbles.length; i ++ ) { bubbles[i].display(); bubbles[i].drift(); } } class Bubble { float x,y; float diameter; float speed; float r,g; // 构造器初始化颜色和大小 // 随机决定位置 Bubble(float r_, float g_, float diameter_) { x = random(width); y = height; r = r_; g = g_; diameter = diameter_; } // 显示气泡 void display() { stroke(0); fill(r,g,255,150); ellipse(x,y,diameter,diameter); } // 移动气泡 void drift() { y += random(-3,-0.1); x += random(-1,1); if (y < -diameter*2) { y = height + diameter*2; } } }
上例中data.txt内的内容为(当然,你可以随便写):
88,149,22
193,78,8
90,152,56
136,18,37
47,2,55
36,142,57
10,61,31
9,121,49
156,60,12
71,200,21
15. 现在我们已经学会舒服的载入信息,然后准备问接下来的这个问题:我们该如何存储信息然后在下一次程序运行的时候载入新信息?比如说,让上例的气泡们当鼠标在其上滚动的时候变化。
// Bubble类中rollover()函数依据引数(mx, my)是否位于气泡内返回一个布尔值(真或假) boolean rollover(int mx, int my) { if (dist(mx,my,x,y) < diameter/2) { return true; } else { return false; } }
我们将鼠标位置(mx, my)到气泡圆心的距离与气泡半径的距离作比较,便可以确定鼠标是否位于圆内。
for (int i = 0; i < bubbles.length; i++) { bubbles[i].display(); bubbles[i].drift(); if (bubbles[i].rollover(mouseX,mouseY)) { bubbles[i].change(); } }
一旦我们执行change()函数以调整Bubble的变量,我们便可以使用p5的saveStrings()函数将新信息存储到一个文本文件中。saveStrings()函数从本质上与loadStrings()相反,它接受一个文件名和一个字符串数组并且将那个数组保存到文件中。
String[] stuff = {"Each String", "will be saved", "on a", "separate line"}; saveStrings("data.txt", stuff);
保存并运行上边这段代码,将在你的文件夹根目录内(注意不是data文件夹)创建如下txt文档。
如果你想要将此文件写入data文件夹,那么你需要指定路径。同样,如果该文件已经存在,它将被重写。
16. OK,具备以上知识之后,我们便可以为气泡例子整一个saveData()函数来改变"data.txt"中的信息。在本例中,我们将在鼠标点击后存储新的数据。
// 一个气泡对象的数组 Bubble[] bubbles; void setup() { size(200,200); smooth(); // 将文本文件作为一个字符串数组载入 String[] data = loadStrings("data.txt"); // 气泡对象数组的大小由文本文件中总的行数决定 bubbles = new Bubble[data.length]; for (int i = 0; i < bubbles.length; i ++ ) { // 每行都被拆分为一个小数数组. float[] values = float(split(data[i], "," )); // 数组中的值被传递入气泡类的构造器. bubbles[i] = new Bubble(values[0],values[1],values[2]); } } void draw() { background(255); // 显示并移动所有气泡 for (int i = 0; i < bubbles.length; i ++ ) { bubbles[i].display(); bubbles[i].drift(); // 如果鼠标在气泡上滚动,则改变气泡 if (bubbles[i].rollover(mouseX,mouseY)) { bubbles[i].change(); } } } // 当鼠标按下时保存新的气泡数据 void mousePressed() { saveData(); } void saveData() { // 对每个气泡存储一个字符串 String[] data = new String[bubbles.length]; for (int i = 0; i < bubbles.length; i ++ ) { // 连接气泡变量 data[i] = bubbles[i].r + " , " + bubbles[i].g + " , " + bubbles[i].diameter; } // 存储到文件 // 指向data文件夹使用saveStrings()重写同一个文件 path to saveStrings(). saveStrings("data/data.txt", data); } // Bubble类 class Bubble { float x,y; float diameter; float speed; float r,g; Bubble(float r_,float g_, float diameter_) { x = random(width); y = height; r = r_; g = g_; diameter = diameter_; } // 检查鼠标是否位于气泡内 boolean rollover(int mx, int my) { if (dist(mx,my,x,y) < diameter/2) { return true; } else { return false; } } // 改变气泡变量 void change() { r = constrain(r + random(-10,10),0,255); g = constrain(g + random(-10,10),0,255); diameter = constrain(diameter + random(-2,4),4,72); } // 显示气泡 void display() { stroke(0); fill(r,g,255,150); ellipse(x,y,diameter,diameter); } // 气泡向上漂流 void drift() { y += random(-3,-0.1); x += random(-1,1); if (y < -diameter*2) { y = height + diameter*2; } } }
17. 我们还可以使用一个统一资源定位器(URL)当作loadStrings()的参数:
String[] lines = loadStrings("http://www.douban.com/artist/oneway");
运行它的结果将返回你输入URL页面的源文件。要继续接下来的学习,你无须是一个HTML专家,但如果你完全不熟悉HTML,你可能需要阅读:http://en.wikipedia.org/wiki/HTML。
18. 不像以逗号划界的文本文件,将HTML源文件存入一个字符串数组是不实用的(每个元素代表源文件中的一行)。将数组转存为一个长的字符串可以让事情简单一些。之前说过的,用join()可以做到这一点:
String onelongstring = join(lines, " " );
19. 当获取到网页源文件后,很多情况我们仅需要其中的一小段。也许是气象信息,股市行情,或者一个新闻标题。我们可以使用indexOf(), substring(), 和length()来为我们从一对文字中找到我们需要的数据。用以下字符串作为例子:
String stuff = "Number of apples:62. Boy, do I like apples or what!";
比如说我们要从以上字符串中提取苹果的数量,我们的算法应该如下:
1)找到子字符串的结尾“apples:”。称其为开始。
2)找到 “apples:”之后的第一个句号。称其为结束。
3)在开始和结束间创建一个子字符串。
4)将字符串转换至一个数字(如果我们希望如此使用它)
写成代码:
// 一个字符串的“结束点”可以通过搜索那个字符串的索引并加上其长度(本例中“apples:”长度为7)来获得 int start = stuff.indexOf("apples:") + 7; // 第一步 int end = stuff.indexOf(".", start); // 第二步 String apples = stuff.substring(start,end); // 第三步 int apple_no = int(apples); // 第四步
20. 以上代码属于小聪明,但我们应当更谨慎,以免因无法找到子字符串而产生错误。我们可以加入一些查错的代码,然后将它们做入一个函数:
// 在两个子字符串间返回一个子字符串的函数 String giveMeTextBetween(String s, String startTag, String endTag) { String found = " "; // 找到开始标签的索引 int startIndex = s.indexOf(startTag); // 如果我们一无所获 if (startIndex == –1) return " "; // 移动到开始标签的末尾 startIndex += startTag.length(); // 找到结束标签的索引 int endIndex = s.indexOf(endTag, startIndex); // 如果我们一无所获 if (endIndex == –1) return " "; // 返回其间的文字 return s.substring(startIndex,endIndex); }
21. 然而了解HTML的同学都该知道,要从它里边提取出有用的数据是如此困难的一件事。其实对于从网站获取数据来说,一个XML(扩展标记语言)的feed将更可靠和易于分析。我们首先从雅虎天气获取XML feed:
http://xml.weather.yahoo.com/forecastrss?p=CHXX0076&u=c
在p5中,从一个XML feed获取数据的方式是使用XML库。但是,为了从一个较低的层面映证字符串分析,作为一个练习,我们将使用我们的loadStrings()手动剥离提取技术。
22. 透过上边那个XML的源代码(建议用Mac的同学使用FireFox查看,Safari无法查看或我还不知道如何查看,因为Safari会主动将这个链接转换为"feed://"打头的),我们可以看到2010年1月28日晚上十点昆明的气温是11摄氏度(也~比起你们来说很暖和吧~)。
气温总在变化而XML的格式不会,因此我们可以推论我们搜索的开始标签应该是:
temp="
结束标签是:
"
23. 知道了开始和结束标签,我们便可使用上边那个giveMeTextBetween()来提取温度了:
String url = "http://xml.weather.yahoo.com/forecastrss?p=CHXX0076&u=c"; String[] lines = loadStrings(url); // 为搜索整个页面而处理数组 String xml = join(lines," "); // 搜索温度 String tag1 = "temp = \""; String tag2 = "\""; temp = int(giveMeTextBetween (xml,tag1,tag2)); println(temp);
注意,要在Java中显示一个引号,需要在要显示引号前加上一个右斜杠:
String q = "这个字符串有一个\"号";
24. 下例从雅虎天气XML feed中检索温度并将之显示于萤幕上。这个例子同样使用了面向对象编程,将所有字符串分析功能做入一个WeatherGrabber类:
PFont f; String[] zips = { "CHXX0076" , "21209" , "90210" }; int counter = 0; // WeatherGrabber对象为我们服务! WeatherGrabber wg; void setup() { size(200,200); // 创建一个WeatherGrabber对象 wg = new WeatherGrabber(zips[counter]); // 告诉它申请天气 wg.requestWeather(); f = createFont( "Lucida" ,16,true); } void draw() { background(255); textFont(f); fill(0); // 获取要显示的值 String weather = wg.getWeather(); int temp = wg.getTemp(); // 显示所有我们想要显示的东西 text(zips[counter],10,160); text(weather,10,90); text(temp,10,40); text("点击转换城市代码. " ,10,180); // 基于温度画一个小温度计 stroke(0); fill(175); rect(10,50,temp*6,20); } void mousePressed() { // 增大计数器并获取下一城市代码的气温 counter = (counter + 1) % zips.length; wg.setZip(zips[counter]); // 每当鼠标点击,这个数据被一个新的城市代码再次请求 wg.requestWeather(); } // WeatherGrabber类 class WeatherGrabber { int temperature = 0; String weather = ""; String zip; WeatherGrabber(String tempZip) { zip = tempZip; } // 设置一个新的城市代码 void setZip(String tempZip) { zip = tempZip; } // 获取温度 int getTemp() { return temperature; } // 获取天气 String getWeather() { return weather; } // 创建实际的XML请求 void requestWeather() { // 将所有HTML/XML源代码放入一个字符串数组 // (每行是数组中的一个元素) String url = "http://xml.weather.yahoo.com/forecastrss?p=" + zip + "&u=c"; String[] lines = loadStrings(url); // 将数组转入一个长的字符串 String xml = join(lines, ""); // 搜索气候状况 String lookfor = "yweather:condition text= \""; String end = "\""; weather = giveMeTextBetween(xml,lookfor,end); // 搜索气温 lookfor = "temp=\""; temperature = int(giveMeTextBetween (xml,lookfor,end)); } // 一个从两个子字符串之间返回子字符串的函数 String giveMeTextBetween(String s, String before, String after) { String found = ""; int start = s.indexOf(before); // 找到开始标签的索引 if (start == - 1) return""; // 如果一无所获,返回一个空字符串 start += before.length(); // 移动到开始标签的末尾 int end = s.indexOf(after,start); // 找到结束标签的索引 if (end == -1) return""; // 如果一无所获,返回一个空字符串 return s.substring(start,end); // 返回两者之间的文本 } }
无限崩溃中,碎了 T_T
连接外部文本后如何逐行显示呢
你好!请问,Table怎么用啊,我在《可视化数据》一书看到的,我按书上写了代码,也下载了相关数据文件放在DATA里,但是processing说不能发现找到Table,是怎么回事啊?我用的版本是1.21。
请再进一步阐释你的问题