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。
请再进一步阐释你的问题